aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2012-04-10 18:11:30 +0100
committerQt by Nokia <qt-info@nokia.com>2012-05-04 13:15:01 +0200
commit6f3bda0dce945a5fc75d8ebad302820fe9979d9b (patch)
tree6581aad8a7fb21ccbebe09d23c30af0e3236e266 /src/qml/qml
parent44f9412bf789d73dd462292038686f5b07026132 (diff)
Initial bundle support
Change-Id: I095249f64ecf4ef1e3fbfb164e3d50edffab61e8 Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/parser/parser.pri4
-rw-r--r--src/qml/qml/qml.pri4
-rw-r--r--src/qml/qml/qqmlbundle.cpp319
-rw-r--r--src/qml/qml/qqmlbundle_p.h122
-rw-r--r--src/qml/qml/qqmlcompiler.cpp10
-rw-r--r--src/qml/qml/qqmlcompiler_p.h2
-rw-r--r--src/qml/qml/qqmldirparser.cpp48
-rw-r--r--src/qml/qml/qqmldirparser_p.h10
-rw-r--r--src/qml/qml/qqmlengine.cpp38
-rw-r--r--src/qml/qml/qqmlengine.h3
-rw-r--r--src/qml/qml/qqmlengine_p.h3
-rw-r--r--src/qml/qml/qqmlfile.cpp804
-rw-r--r--src/qml/qml/qqmlfile.h124
-rw-r--r--src/qml/qml/qqmlimport.cpp969
-rw-r--r--src/qml/qml/qqmlimport_p.h24
-rw-r--r--src/qml/qml/qqmlscript.cpp63
-rw-r--r--src/qml/qml/qqmlscript_p.h14
-rw-r--r--src/qml/qml/qqmltypeloader.cpp312
-rw-r--r--src/qml/qml/qqmltypeloader_p.h100
-rw-r--r--src/qml/qml/qquickworkerscript.cpp3
-rw-r--r--src/qml/qml/v8/qv8include.cpp3
21 files changed, 2347 insertions, 632 deletions
diff --git a/src/qml/qml/parser/parser.pri b/src/qml/qml/parser/parser.pri
index 6be85ba85a..7dba8bc92a 100644
--- a/src/qml/qml/parser/parser.pri
+++ b/src/qml/qml/parser/parser.pri
@@ -8,7 +8,7 @@ HEADERS += \
$$PWD/qqmljsmemorypool_p.h \
$$PWD/qqmljsparser_p.h \
$$PWD/qqmljsglobal_p.h \
- $$PWD/qqmljskeywords_p.h
+ $$PWD/qqmljskeywords_p.h \
SOURCES += \
$$PWD/qqmljsast.cpp \
@@ -16,4 +16,4 @@ SOURCES += \
$$PWD/qqmljsengine_p.cpp \
$$PWD/qqmljsgrammar.cpp \
$$PWD/qqmljslexer.cpp \
- $$PWD/qqmljsparser.cpp
+ $$PWD/qqmljsparser.cpp \
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 1237740f31..30acea1321 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -51,6 +51,8 @@ SOURCES += \
$$PWD/qqmlabstractbinding.cpp \
$$PWD/qqmlvaluetypeproxybinding.cpp \
$$PWD/qqmlglobal.cpp \
+ $$PWD/qqmlfile.cpp \
+ $$PWD/qqmlbundle.cpp \
HEADERS += \
$$PWD/qqmlglobal_p.h \
@@ -122,6 +124,8 @@ HEADERS += \
$$PWD/qqmljavascriptexpression_p.h \
$$PWD/qqmlabstractbinding_p.h \
$$PWD/qqmlvaluetypeproxybinding_p.h \
+ $$PWD/qqmlfile.h \
+ $$PWD/qqmlbundle_p.h \
include(parser/parser.pri)
include(rewriter/rewriter.pri)
diff --git a/src/qml/qml/qqmlbundle.cpp b/src/qml/qml/qqmlbundle.cpp
new file mode 100644
index 0000000000..fa47cb505d
--- /dev/null
+++ b/src/qml/qml/qqmlbundle.cpp
@@ -0,0 +1,319 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module 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 "qqmlbundle_p.h"
+#include <QtCore/QtCore>
+#include <iostream>
+#include <cstdlib>
+
+static const unsigned char qmlBundleHeaderData[] = { 255, 'q', 'm', 'l', 'd', 'i', 'r', 255 };
+static const unsigned int qmlBundleHeaderLength = 8;
+
+//
+// Entries
+//
+QString QQmlBundle::FileEntry::fileName() const
+{
+ return QString((QChar *)&data[0], fileNameLength / sizeof(QChar));
+}
+
+bool QQmlBundle::FileEntry::isFileName(const QString &fileName) const
+{
+ return fileName.length() * sizeof(QChar) == (unsigned)fileNameLength &&
+ 0 == ::memcmp(fileName.constData(), &data[0], fileNameLength);
+}
+
+const char *QQmlBundle::FileEntry::contents() const {
+ return &data[fileNameLength];
+}
+
+quint64 QQmlBundle::FileEntry::fileSize() const
+{
+ return size - (sizeof(FileEntry) + fileNameLength);
+}
+
+
+//
+// QQmlBundle
+//
+QQmlBundle::QQmlBundle(const QString &fileName)
+: file(fileName),
+ buffer(0),
+ bufferSize(0),
+ opened(false),
+ headerWritten(false)
+{
+}
+
+QQmlBundle::~QQmlBundle()
+{
+ close();
+}
+
+bool QQmlBundle::open(QIODevice::OpenMode mode)
+{
+ if (!opened) {
+ if (!file.open(mode))
+ return false;
+
+ bufferSize = file.size();
+ buffer = file.map(0, bufferSize);
+
+ if (bufferSize == 0 ||
+ (bufferSize >= 8 && 0 == ::memcmp(buffer, qmlBundleHeaderData, qmlBundleHeaderLength))) {
+ opened = true;
+ headerWritten = false;
+ return true;
+ } else {
+ close();
+ return false;
+ }
+ }
+ return true;
+}
+
+void QQmlBundle::close()
+{
+ if (opened) {
+ opened = false;
+ headerWritten = false;
+ file.unmap(buffer);
+ file.close();
+ }
+}
+
+QList<const QQmlBundle::FileEntry *> QQmlBundle::files() const
+{
+ QList<const FileEntry *> files;
+ const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
+ const char *end = (const char *) buffer + bufferSize;
+
+ while (ptr < end) {
+ const Entry *cmd = (const Entry *) ptr;
+
+ switch (static_cast<Entry::Kind>(cmd->kind)) {
+ case Entry::File: {
+ const FileEntry *f = reinterpret_cast<const FileEntry *>(cmd);
+ files.append(f);
+ } break;
+
+ case Entry::Link:
+ case Entry::Skip: {
+ // Skip
+ } break;
+
+ default:
+ // throw an error
+ return QList<const FileEntry *>();
+ } // switch
+
+ ptr += cmd->size;
+ Q_ASSERT(ptr <= end); // throw an error
+ }
+ return files;
+}
+
+void QQmlBundle::remove(const FileEntry *entry)
+{
+ Q_ASSERT(entry->kind == Entry::File); // ### throw an error
+ Q_ASSERT(file.isWritable());
+ const_cast<FileEntry *>(entry)->kind = Entry::Skip;
+}
+
+int QQmlBundle::bundleHeaderLength()
+{
+ return qmlBundleHeaderLength;
+}
+
+bool QQmlBundle::isBundleHeader(const char *data, int size)
+{
+ if ((unsigned int)size < qmlBundleHeaderLength)
+ return false;
+
+ return 0 == ::memcmp(data, qmlBundleHeaderData, qmlBundleHeaderLength);
+}
+
+//
+// find a some empty space we can use to insert new entries.
+//
+const QQmlBundle::Entry *QQmlBundle::findInsertPoint(quint64 size, qint64 *offset)
+{
+ const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
+ const char *end = (const char *) buffer + bufferSize;
+
+ while (ptr < end) {
+ const Entry *cmd = (const Entry *) ptr;
+
+ if (cmd->kind == Entry::Skip && size + sizeof(RawEntry) < cmd->size) {
+ *offset = ptr - ((const char *) buffer + qmlBundleHeaderLength);
+ return cmd;
+ }
+
+ ptr += cmd->size;
+ Q_ASSERT(ptr <= end); // throw an error
+ }
+
+ return 0;
+}
+
+const QQmlBundle::FileEntry *QQmlBundle::find(const QString &fileName) const
+{
+ const char *ptr = (const char *) buffer + qmlBundleHeaderLength;
+ const char *end = (const char *) buffer + bufferSize;
+
+ while (ptr < end) {
+ const Entry *cmd = (const Entry *) ptr;
+
+ if (cmd->kind == Entry::File) {
+ const FileEntry *fileEntry = static_cast<const FileEntry *>(cmd);
+
+ if (fileEntry->isFileName(fileName))
+ return fileEntry;
+ }
+
+ ptr += cmd->size;
+ Q_ASSERT(ptr <= end); // throw an error
+ }
+
+ return 0;
+}
+
+const QQmlBundle::FileEntry *QQmlBundle::link(const FileEntry *entry, const QString &linkName) const
+{
+ const char *ptr = (const char *) buffer + entry->link;
+
+ while (ptr != (const char *)buffer) {
+ const Entry *cmd = (const Entry *) ptr;
+ Q_ASSERT(cmd->kind == Entry::Link);
+
+ const FileEntry *fileEntry = static_cast<const FileEntry *>(cmd);
+ if (fileEntry->fileName() == linkName)
+ return fileEntry;
+
+ ptr = (const char *) buffer + fileEntry->link;
+ }
+
+ return 0;
+}
+
+const QQmlBundle::FileEntry *QQmlBundle::find(const QChar *fileName, int length) const
+{
+ return find(QString::fromRawData(fileName, length));
+}
+
+bool QQmlBundle::add(const QString &name, const QString &fileName)
+{
+ if (!file.isWritable())
+ return false;
+ else if (find(fileName))
+ return false;
+
+ QFile inputFile(fileName);
+ if (!inputFile.open(QFile::ReadOnly))
+ return false;
+
+ // ### use best-fit algorithm
+ if (!file.atEnd())
+ file.seek(file.size());
+
+ FileEntry cmd;
+ const quint64 inputFileSize = inputFile.size();
+
+ cmd.kind = Entry::File;
+ cmd.link = 0;
+ cmd.size = sizeof(FileEntry) + name.length() * sizeof(QChar) + inputFileSize;
+ cmd.fileNameLength = name.length() * sizeof(QChar);
+
+ if (bufferSize == 0 && headerWritten == false) {
+ file.write((const char *)qmlBundleHeaderData, qmlBundleHeaderLength);
+ headerWritten = true;
+ }
+
+ file.write((const char *) &cmd, sizeof(FileEntry));
+ file.write((const char *) name.constData(), name.length() * sizeof(QChar));
+
+ uchar *source = inputFile.map(0, inputFileSize);
+ file.write((const char *) source, inputFileSize);
+ inputFile.unmap(source);
+ return true;
+}
+
+bool QQmlBundle::add(const QString &fileName)
+{
+ return add(fileName, fileName);
+}
+
+bool QQmlBundle::addMetaLink(const QString &fileName,
+ const QString &linkName,
+ const QByteArray &data)
+{
+ if (!file.isWritable())
+ return false;
+
+ const FileEntry *fileEntry = find(fileName);
+ if (!fileEntry)
+ return false;
+
+ // ### use best-fit algorithm
+ if (!file.atEnd())
+ file.seek(file.size());
+
+ FileEntry cmd;
+
+ const quint64 inputFileSize = data.size();
+
+ cmd.kind = Entry::Link;
+ cmd.link = fileEntry->link;
+ cmd.size = sizeof(FileEntry) + linkName.length() * sizeof(QChar) + inputFileSize;
+ cmd.fileNameLength = linkName.length() * sizeof(QChar);
+
+ if (bufferSize == 0 && headerWritten == false) {
+ file.write((const char *)qmlBundleHeaderData, qmlBundleHeaderLength);
+ headerWritten = true;
+ }
+
+ const_cast<FileEntry *>(fileEntry)->link = file.size();
+
+ file.write((const char *) &cmd, sizeof(FileEntry));
+ file.write((const char *) linkName.constData(), linkName.length() * sizeof(QChar));
+ file.write((const char *) data.constData(), inputFileSize);
+ return true;
+}
diff --git a/src/qml/qml/qqmlbundle_p.h b/src/qml/qml/qqmlbundle_p.h
new file mode 100644
index 0000000000..029acf1f1b
--- /dev/null
+++ b/src/qml/qml/qqmlbundle_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module 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$
+**
+****************************************************************************/
+
+#ifndef QQMLBUNDLE_P_H
+#define QQMLBUNDLE_P_H
+
+#include <QtCore/qfile.h>
+#include <QtCore/qstring.h>
+#include <QtQml/qtqmlglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_EXPORT QQmlBundle
+{
+ Q_DISABLE_COPY(QQmlBundle)
+public:
+ struct Q_PACKED Q_QML_EXPORT Entry
+ {
+ enum Kind {
+ File = 123, // Normal file
+ Skip, // Empty space
+ Link // A meta data linked file
+
+ // ### add entries for qmldir, index, ...
+ };
+
+ int kind;
+ quint64 size;
+ };
+
+ struct Q_PACKED Q_QML_EXPORT RawEntry : public Entry
+ {
+ char data[]; // trailing data
+ };
+
+ struct Q_PACKED Q_QML_EXPORT FileEntry : public Entry
+ {
+ quint64 link;
+ int fileNameLength;
+ char data[]; // trailing data
+
+ QString fileName() const;
+ bool isFileName(const QString &) const;
+
+ quint64 fileSize() const;
+ const char *contents() const;
+ };
+
+ QQmlBundle(const QString &fileName);
+ ~QQmlBundle();
+
+ bool open(QIODevice::OpenMode mode = QIODevice::ReadWrite);
+ void close();
+
+ QList<const FileEntry *> files() const;
+ void remove(const FileEntry *entry);
+ bool add(const QString &fileName);
+ bool add(const QString &name, const QString &fileName);
+
+ bool addMetaLink(const QString &fileName,
+ const QString &linkName,
+ const QByteArray &data);
+
+ const FileEntry *find(const QString &fileName) const;
+ const FileEntry *find(const QChar *fileName, int length) const;
+
+ const FileEntry *link(const FileEntry *, const QString &linkName) const;
+
+ static int bundleHeaderLength();
+ static bool isBundleHeader(const char *, int size);
+private:
+ const Entry *findInsertPoint(quint64 size, qint64 *offset);
+
+private:
+ QFile file;
+ uchar *buffer;
+ quint64 bufferSize;
+ bool opened:1;
+ bool headerWritten:1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLBUNDLE_P_H
diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp
index 8c47c7c0c5..5708a84fd9 100644
--- a/src/qml/qml/qqmlcompiler.cpp
+++ b/src/qml/qml/qqmlcompiler.cpp
@@ -1742,7 +1742,7 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop,
}
QQmlType *type = 0;
- QQmlImportedNamespace *typeNamespace = 0;
+ QQmlImportNamespace *typeNamespace = 0;
unit->imports().resolveType(prop->name().toString(), &type, 0, 0, 0, &typeNamespace);
if (typeNamespace) {
@@ -1849,10 +1849,10 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop,
return true;
}
-bool QQmlCompiler::buildPropertyInNamespace(QQmlImportedNamespace *ns,
- QQmlScript::Property *nsProp,
- QQmlScript::Object *obj,
- const BindingContext &ctxt)
+bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns,
+ QQmlScript::Property *nsProp,
+ QQmlScript::Object *obj,
+ const BindingContext &ctxt)
{
if (!nsProp->value)
COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace"));
diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h
index 3b6fdf1473..5a52dcf403 100644
--- a/src/qml/qml/qqmlcompiler_p.h
+++ b/src/qml/qml/qqmlcompiler_p.h
@@ -318,7 +318,7 @@ private:
const QQmlCompilerTypes::BindingContext &);
bool buildProperty(QQmlScript::Property *prop, QQmlScript::Object *obj,
const QQmlCompilerTypes::BindingContext &);
- bool buildPropertyInNamespace(QQmlImportedNamespace *ns,
+ bool buildPropertyInNamespace(QQmlImportNamespace *ns,
QQmlScript::Property *prop,
QQmlScript::Object *obj,
const QQmlCompilerTypes::BindingContext &);
diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp
index 82289311c9..2da4a3300b 100644
--- a/src/qml/qml/qqmldirparser.cpp
+++ b/src/qml/qml/qqmldirparser.cpp
@@ -43,6 +43,7 @@
#include "qqmlerror.h"
#include "qqmlglobal_p.h"
+#include <QtQml/qqmlfile.h>
#include <QtCore/QTextStream>
#include <QtCore/QFile>
#include <QtCore/QtDebug>
@@ -58,26 +59,6 @@ QQmlDirParser::~QQmlDirParser()
{
}
-QUrl QQmlDirParser::url() const
-{
- return _url;
-}
-
-void QQmlDirParser::setUrl(const QUrl &url)
-{
- _url = url;
-}
-
-QString QQmlDirParser::fileSource() const
-{
- return _filePathSouce;
-}
-
-void QQmlDirParser::setFileSource(const QString &filePath)
-{
- _filePathSouce = filePath;
-}
-
QString QQmlDirParser::source() const
{
return _source;
@@ -94,6 +75,9 @@ bool QQmlDirParser::isParsed() const
return _isParsed;
}
+/*!
+\a url is used for generating errors.
+*/
bool QQmlDirParser::parse()
{
if (_isParsed)
@@ -105,23 +89,6 @@ bool QQmlDirParser::parse()
_components.clear();
_scripts.clear();
- if (_source.isEmpty() && !_filePathSouce.isEmpty()) {
- QFile file(_filePathSouce);
- if (!QQml_isFileCaseCorrect(_filePathSouce)) {
- QQmlError error;
- error.setDescription(QString::fromUtf8("cannot load module \"$$URI$$\": File name case mismatch for \"%1\"").arg(_filePathSouce));
- _errors.prepend(error);
- return false;
- } else if (file.open(QFile::ReadOnly)) {
- _source = QString::fromUtf8(file.readAll());
- } else {
- QQmlError error;
- error.setDescription(QString::fromUtf8("module \"$$URI$$\" definition \"%1\" not readable").arg(_filePathSouce));
- _errors.prepend(error);
- return false;
- }
- }
-
QTextStream stream(&_source);
int lineNumber = 0;
@@ -246,7 +213,6 @@ bool QQmlDirParser::parse()
void QQmlDirParser::reportError(int line, int column, const QString &description)
{
QQmlError error;
- error.setUrl(_url);
error.setLine(line);
error.setColumn(column);
error.setDescription(description);
@@ -261,6 +227,12 @@ bool QQmlDirParser::hasError() const
return false;
}
+void QQmlDirParser::setError(const QQmlError &e)
+{
+ _errors.clear();
+ _errors.append(e);
+}
+
QList<QQmlError> QQmlDirParser::errors(const QString &uri) const
{
QList<QQmlError> errors = _errors;
diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h
index f46e1781da..77fe277a7e 100644
--- a/src/qml/qml/qqmldirparser_p.h
+++ b/src/qml/qml/qqmldirparser_p.h
@@ -60,6 +60,7 @@
QT_BEGIN_NAMESPACE
class QQmlError;
+class QQmlEngine;
class QQmlDirParser
{
Q_DISABLE_COPY(QQmlDirParser)
@@ -68,12 +69,6 @@ public:
QQmlDirParser();
~QQmlDirParser();
- QUrl url() const;
- void setUrl(const QUrl &url);
-
- QString fileSource() const;
- void setFileSource(const QString &filePath);
-
QString source() const;
void setSource(const QString &source);
@@ -81,6 +76,7 @@ public:
bool parse();
bool hasError() const;
+ void setError(const QQmlError &);
QList<QQmlError> errors(const QString &uri) const;
struct Plugin
@@ -146,9 +142,7 @@ private:
private:
QList<QQmlError> _errors;
- QUrl _url;
QString _source;
- QString _filePathSouce;
QList<Component> _components;
QList<Script> _scripts;
QList<Plugin> _plugins;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 91c86a249a..1c0026981d 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -1303,44 +1303,6 @@ void QQmlData::setBindingBit(QObject *obj, int bit)
bindingBits[bit / 32] |= (1 << (bit % 32));
}
-QString QQmlEnginePrivate::urlToLocalFileOrQrc(const QUrl& url)
-{
- if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
- if (url.authority().isEmpty())
- return QLatin1Char(':') + url.path();
- return QString();
- }
- return url.toLocalFile();
-}
-
-
-static QString toLocalFile(const QString &url)
-{
- if (!url.startsWith(QLatin1String("file://"), Qt::CaseInsensitive))
- return QString();
-
- QString file = url.mid(7);
-
- //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
-
- // magic for drives on windows
- if (file.length() > 2 && file.at(0) == QLatin1Char('/') && file.at(2) == QLatin1Char(':'))
- file.remove(0, 1);
-
- return file;
-}
-
-QString QQmlEnginePrivate::urlToLocalFileOrQrc(const QString& url)
-{
- if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
- if (url.length() > 4)
- return QLatin1Char(':') + url.mid(4);
- return QString();
- }
-
- return toLocalFile(url);
-}
-
void QQmlEnginePrivate::sendQuit()
{
Q_Q(QQmlEngine);
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index 21a03d63ce..8d250ed209 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -104,6 +104,8 @@ public:
void setPluginPathList(const QStringList &paths);
void addPluginPath(const QString& dir);
+ bool addNamedBundle(const QString &name, const QString &fileName);
+
bool importPlugin(const QString &filePath, const QString &uri, QString *errorString); // XXX: Qt 5: Remove this function
bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors);
@@ -136,7 +138,6 @@ public:
enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
static void setObjectOwnership(QObject *, ObjectOwnership);
static ObjectOwnership objectOwnership(QObject *);
-
protected:
virtual bool event(QEvent *);
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 2521888668..4cfb8c772a 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -256,9 +256,6 @@ public:
inline static QQmlEnginePrivate *get(QQmlContextData *c);
inline static QQmlEngine *get(QQmlEnginePrivate *p);
- static QString urlToLocalFileOrQrc(const QUrl& url);
- static QString urlToLocalFileOrQrc(const QString& url);
-
static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor);
static void defineModule();
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
new file mode 100644
index 0000000000..6d48536b2b
--- /dev/null
+++ b/src/qml/qml/qqmlfile.cpp
@@ -0,0 +1,804 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module 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 "qqmlfile.h"
+
+#include <QtCore/qurl.h>
+#include <QtCore/qobject.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlglobal_p.h>
+
+/*!
+\class The QQmlFile class gives access to local and remote files.
+
+Supports file://, qrc://, bundle:// uris and whatever QNetworkAccessManager supports.
+*/
+
+#define QQMLFILE_MAX_REDIRECT_RECURSION 16
+
+QT_BEGIN_NAMESPACE
+
+static QString qrc_string(QLatin1String("qrc"));
+static QString file_string(QLatin1String("file"));
+static QString bundle_string(QLatin1String("bundle"));
+
+class QQmlFilePrivate;
+class QQmlFileNetworkReply : public QObject
+{
+Q_OBJECT
+public:
+ QQmlFileNetworkReply(QQmlEngine *, QQmlFilePrivate *, const QUrl &);
+ ~QQmlFileNetworkReply();
+
+signals:
+ void finished();
+ void downloadProgress(qint64, qint64);
+
+public slots:
+ void networkFinished();
+ void networkDownloadProgress(qint64, qint64);
+
+public:
+ static int finishedIndex;
+ static int downloadProgressIndex;
+ static int networkFinishedIndex;
+ static int networkDownloadProgressIndex;
+ static int replyFinishedIndex;
+ static int replyDownloadProgressIndex;
+
+private:
+ QQmlEngine *m_engine;
+ QQmlFilePrivate *m_p;
+
+ int m_redirectCount;
+ QNetworkReply *m_reply;
+};
+
+class QQmlFilePrivate
+{
+public:
+ QQmlFilePrivate();
+
+ mutable QUrl url;
+ mutable QString urlString;
+
+ QQmlBundleData *bundle;
+ const QQmlBundle::FileEntry *file;
+
+ QByteArray data;
+
+ enum Error {
+ None, NotFound, CaseMismatch, Network
+ };
+
+ Error error;
+ QString errorString;
+
+ QQmlFileNetworkReply *reply;
+};
+
+int QQmlFileNetworkReply::finishedIndex = -1;
+int QQmlFileNetworkReply::downloadProgressIndex = -1;
+int QQmlFileNetworkReply::networkFinishedIndex = -1;
+int QQmlFileNetworkReply::networkDownloadProgressIndex = -1;
+int QQmlFileNetworkReply::replyFinishedIndex = -1;
+int QQmlFileNetworkReply::replyDownloadProgressIndex = -1;
+
+QQmlFileNetworkReply::QQmlFileNetworkReply(QQmlEngine *e, QQmlFilePrivate *p, const QUrl &url)
+: m_engine(e), m_p(p), m_redirectCount(0), m_reply(0)
+{
+ if (finishedIndex == -1) {
+ const QMetaObject *smo = &staticMetaObject;
+ finishedIndex = smo->indexOfSignal("finished()");
+ downloadProgressIndex = smo->indexOfSignal("downloadProgress(qint64,qint64)");
+ networkFinishedIndex = smo->indexOfMethod("networkFinished()");
+ networkDownloadProgressIndex = smo->indexOfMethod("networkDownloadProgress(qint64,qint64)");
+
+ const QMetaObject *rsmo = &QNetworkReply::staticMetaObject;
+ replyFinishedIndex = rsmo->indexOfSignal("finished()");
+ replyDownloadProgressIndex = rsmo->indexOfSignal("downloadProgress(qint64,qint64)");
+ }
+ Q_ASSERT(finishedIndex != -1 && downloadProgressIndex != -1 &&
+ networkFinishedIndex != -1 && networkDownloadProgressIndex != -1 &&
+ replyFinishedIndex != -1 && replyDownloadProgressIndex != -1);
+
+ QNetworkRequest req(url);
+ req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+
+ m_reply = m_engine->networkAccessManager()->get(req);
+ QMetaObject::connect(m_reply, replyFinishedIndex, this, networkFinishedIndex);
+ QMetaObject::connect(m_reply, replyDownloadProgressIndex, this, networkDownloadProgressIndex);
+}
+
+QQmlFileNetworkReply::~QQmlFileNetworkReply()
+{
+ if (m_reply) {
+ m_reply->disconnect();
+ m_reply->deleteLater();
+ }
+}
+
+void QQmlFileNetworkReply::networkFinished()
+{
+ ++m_redirectCount;
+ if (m_redirectCount < QQMLFILE_MAX_REDIRECT_RECURSION) {
+ QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirect.isValid()) {
+ QUrl url = m_reply->url().resolved(redirect.toUrl());
+
+ QNetworkRequest req(url);
+ req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+
+ m_reply->deleteLater();
+ m_reply = m_engine->networkAccessManager()->get(req);
+
+ QMetaObject::connect(m_reply, replyFinishedIndex,
+ this, networkFinishedIndex);
+ QMetaObject::connect(m_reply, replyDownloadProgressIndex,
+ this, networkDownloadProgressIndex);
+
+ return;
+ }
+ }
+
+ if (m_reply->error()) {
+ m_p->errorString = m_reply->errorString();
+ m_p->error = QQmlFilePrivate::Network;
+ } else {
+ m_p->data = m_reply->readAll();
+ }
+
+ m_reply->deleteLater();
+ m_reply = 0;
+
+ m_p->reply = 0;
+ emit finished();
+ delete this;
+}
+
+void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
+{
+ emit downloadProgress(a, b);
+}
+
+QQmlFilePrivate::QQmlFilePrivate()
+: bundle(0), file(0), error(None), reply(0)
+{
+}
+
+QQmlFile::QQmlFile()
+: d(new QQmlFilePrivate)
+{
+}
+
+QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
+: d(new QQmlFilePrivate)
+{
+ load(e, url);
+}
+
+QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
+: d(new QQmlFilePrivate)
+{
+ load(e, url);
+}
+
+QQmlFile::~QQmlFile()
+{
+ if (d->reply)
+ delete d->reply;
+ if (d->bundle)
+ d->bundle->release();
+
+ delete d;
+ d = 0;
+}
+
+bool QQmlFile::isNull() const
+{
+ return status() == Null;
+}
+
+bool QQmlFile::isReady() const
+{
+ return status() == Ready;
+}
+
+bool QQmlFile::isError() const
+{
+ return status() == Error;
+}
+
+bool QQmlFile::isLoading() const
+{
+ return status() == Loading;
+}
+
+QUrl QQmlFile::url() const
+{
+ if (!d->urlString.isEmpty()) {
+ d->url = QUrl(d->urlString);
+ d->urlString = QString();
+ }
+ return d->url;
+}
+
+QQmlFile::Status QQmlFile::status() const
+{
+ if (d->url.isEmpty() && d->urlString.isEmpty())
+ return Null;
+ else if (d->reply)
+ return Loading;
+ else if (d->error != QQmlFilePrivate::None)
+ return Error;
+ else
+ return Ready;
+}
+
+QString QQmlFile::error() const
+{
+ switch (d->error) {
+ default:
+ case QQmlFilePrivate::None:
+ return QString();
+ case QQmlFilePrivate::NotFound:
+ return QLatin1String("File not found");
+ case QQmlFilePrivate::CaseMismatch:
+ return QLatin1String("File name case mismatch");
+ }
+}
+
+qint64 QQmlFile::size() const
+{
+ if (d->file) return d->file->fileSize();
+ else return d->data.size();
+}
+
+const char *QQmlFile::data() const
+{
+ if (d->file) return d->file->contents();
+ else return d->data.constData();
+}
+
+QByteArray QQmlFile::dataByteArray() const
+{
+ if (d->file) return QByteArray(d->file->contents(), d->file->fileSize());
+ else return d->data;
+}
+
+QByteArray QQmlFile::metaData(const QString &name) const
+{
+ if (d->file) {
+ Q_ASSERT(d->bundle);
+ const QQmlBundle::FileEntry *meta = d->bundle->link(d->file, name);
+ if (meta)
+ return QByteArray::fromRawData(meta->contents(), meta->fileSize());
+ }
+ return QByteArray();
+}
+
+void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
+{
+ Q_ASSERT(engine);
+
+ QString scheme = url.scheme();
+
+ clear();
+ d->url = url;
+
+ if (isBundle(url)) {
+ // Bundle
+ QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
+ QQmlBundleData *bundle = p->typeLoader.getBundle(url.host());
+
+ d->error = QQmlFilePrivate::NotFound;
+
+ if (bundle) {
+ QString filename = url.path().mid(1);
+ const QQmlBundle::FileEntry *entry = bundle->find(filename);
+ if (entry) {
+ d->file = entry;
+ d->bundle = bundle;
+ d->bundle->addref();
+ d->error = QQmlFilePrivate::None;
+ }
+ bundle->release();
+ }
+ } else if (isLocalFile(url)) {
+ QString lf = urlToLocalFileOrQrc(url);
+
+ if (!QQml_isFileCaseCorrect(lf)) {
+ d->error = QQmlFilePrivate::CaseMismatch;
+ return;
+ }
+
+ QFile file(lf);
+ if (file.open(QFile::ReadOnly)) {
+ d->data = file.readAll();
+ } else {
+ d->error = QQmlFilePrivate::NotFound;
+ }
+ } else {
+ d->reply = new QQmlFileNetworkReply(engine, d, url);
+ }
+}
+
+void QQmlFile::load(QQmlEngine *engine, const QString &url)
+{
+ Q_ASSERT(engine);
+
+ clear();
+
+ d->urlString = url;
+
+ if (isBundle(url)) {
+ // Bundle
+ QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine);
+
+ d->error = QQmlFilePrivate::NotFound;
+
+ int index = url.indexOf(QLatin1Char('/'), 9);
+ if (index == -1)
+ return;
+
+ QStringRef identifier(&url, 9, index - 9);
+
+ QQmlBundleData *bundle = p->typeLoader.getBundle(identifier);
+
+ d->error = QQmlFilePrivate::NotFound;
+
+ if (bundle) {
+ QString filename = url.mid(index);
+ const QQmlBundle::FileEntry *entry = bundle->find(filename);
+ if (entry) {
+ d->data = QByteArray(entry->contents(), entry->fileSize());
+ d->error = QQmlFilePrivate::None;
+ }
+ bundle->release();
+ }
+
+ } else if (isLocalFile(url)) {
+ QString lf = urlToLocalFileOrQrc(url);
+
+ if (!QQml_isFileCaseCorrect(lf)) {
+ d->error = QQmlFilePrivate::CaseMismatch;
+ return;
+ }
+
+ QFile file(lf);
+ if (file.open(QFile::ReadOnly)) {
+ d->data = file.readAll();
+ } else {
+ d->error = QQmlFilePrivate::NotFound;
+ }
+ } else {
+ QUrl qurl(url);
+ d->url = qurl;
+ d->urlString = QString();
+ d->reply = new QQmlFileNetworkReply(engine, d, qurl);
+ }
+}
+
+void QQmlFile::clear()
+{
+ d->url = QUrl();
+ d->urlString = QString();
+ d->data = QByteArray();
+ if (d->bundle) d->bundle->release();
+ d->bundle = 0;
+ d->file = 0;
+ d->error = QQmlFilePrivate::None;
+}
+
+void QQmlFile::clear(QObject *)
+{
+ clear();
+}
+
+bool QQmlFile::connectFinished(QObject *object, const char *method)
+{
+ if (!d || !d->reply) {
+ qWarning("QQmlFile: connectFinished() called when not loading.");
+ return false;
+ }
+
+ return QObject::connect(d->reply, SIGNAL(finished()),
+ object, method);
+}
+
+bool QQmlFile::connectFinished(QObject *object, int method)
+{
+ if (!d || !d->reply) {
+ qWarning("QQmlFile: connectFinished() called when not loading.");
+ return false;
+ }
+
+ return QMetaObject::connect(d->reply, QQmlFileNetworkReply::finishedIndex,
+ object, method);
+}
+
+bool QQmlFile::connectDownloadProgress(QObject *object, const char *method)
+{
+ if (!d || !d->reply) {
+ qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
+ return false;
+ }
+
+ return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
+ object, method);
+}
+
+bool QQmlFile::connectDownloadProgress(QObject *object, int method)
+{
+ if (!d || !d->reply) {
+ qWarning("QQmlFile: connectDownloadProgress() called when not loading.");
+ return false;
+ }
+
+ return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
+ object, method);
+}
+
+/*!
+Returns true if QQmlFile will open \a url synchronously.
+
+Synchronous urls have a qrc://, file://, or bundle:// scheme.
+*/
+bool QQmlFile::isSynchronous(const QUrl &url)
+{
+ QString scheme = url.scheme();
+
+ if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
+ (scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive)) ||
+ (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
+ return true;
+ else
+ return false;
+}
+
+/*!
+Returns true if QQmlFile will open \a url synchronously.
+
+Synchronous urls have a qrc://, file://, or bundle:// scheme.
+*/
+bool QQmlFile::isSynchronous(const QString &url)
+{
+ if (url.length() < 6 /* qrc:// */)
+ return false;
+
+ QChar f = url[0];
+
+ if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
+
+ return url.length() >= 7 /* file:// */ &&
+ url.startsWith(file_string, Qt::CaseInsensitive) &&
+ url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
+
+ } else if (f == QLatin1Char('b') || f == QLatin1Char('B')) {
+
+ return url.length() >= 9 /* bundle:// */ &&
+ url.startsWith(bundle_string, Qt::CaseInsensitive) &&
+ url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
+
+ } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
+
+ return url.length() >= 6 /* bundle:// */ &&
+ url.startsWith(qrc_string, Qt::CaseInsensitive) &&
+ url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/') && url[5] == QLatin1Char('/');
+
+ }
+
+ return false;
+}
+
+/*!
+Returns true if \a url is a bundle.
+
+Bundle urls have a bundle:// scheme.
+*/
+bool QQmlFile::isBundle(const QString &url)
+{
+ return url.length() >= 9 && url.startsWith(bundle_string, Qt::CaseInsensitive) &&
+ url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/') && url[8] == QLatin1Char('/');
+}
+
+/*!
+Returns true if \a url is a bundle.
+
+Bundle urls have a bundle:// scheme.
+*/
+bool QQmlFile::isBundle(const QUrl &url)
+{
+ QString scheme = url.scheme();
+
+ return scheme.length() == 6 && 0 == scheme.compare(bundle_string, Qt::CaseInsensitive);
+}
+
+/*!
+Returns true if \a url is a local file that can be opened with QFile.
+
+Local file urls have either a qrc:// or file:// scheme.
+*/
+bool QQmlFile::isLocalFile(const QUrl &url)
+{
+ QString scheme = url.scheme();
+
+ if ((scheme.length() == 4 && 0 == scheme.compare(file_string, Qt::CaseInsensitive)) ||
+ (scheme.length() == 3 && 0 == scheme.compare(qrc_string, Qt::CaseInsensitive)))
+ return true;
+ else
+ return false;
+}
+
+/*!
+Returns true if \a url is a local file that can be opened with QFile.
+
+Local file urls have either a qrc:// or file:// scheme.
+*/
+bool QQmlFile::isLocalFile(const QString &url)
+{
+ if (url.length() < 6 /* qrc:// */)
+ return false;
+
+ QChar f = url[0];
+
+ if (f == QLatin1Char('f') || f == QLatin1Char('F')) {
+
+ return url.length() >= 7 /* file:// */ &&
+ url.startsWith(file_string, Qt::CaseInsensitive) &&
+ url[4] == QLatin1Char(':') && url[5] == QLatin1Char('/') && url[6] == QLatin1Char('/');
+
+ } else if (f == QLatin1Char('q') || f == QLatin1Char('Q')) {
+
+ return url.length() >= 6 /* bundle:// */ &&
+ url.startsWith(qrc_string, Qt::CaseInsensitive) &&
+ url[3] == QLatin1Char(':') && url[4] == QLatin1Char('/') && url[5] == QLatin1Char('/');
+
+ }
+
+ return false;
+}
+
+/*!
+If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
+empty string.
+*/
+QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url)
+{
+ if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0) {
+ if (url.authority().isEmpty())
+ return QLatin1Char(':') + url.path();
+ return QString();
+ }
+ return url.toLocalFile();
+}
+
+static QString toLocalFile(const QString &url)
+{
+ if (!url.startsWith(QLatin1String("file://"), Qt::CaseInsensitive))
+ return QString();
+
+ QString file = url.mid(7);
+
+ //XXX TODO: handle windows hostnames: "//servername/path/to/file.txt"
+
+ // magic for drives on windows
+ if (file.length() > 2 && file.at(0) == QLatin1Char('/') && file.at(2) == QLatin1Char(':'))
+ file.remove(0, 1);
+
+ return file;
+}
+
+/*!
+If \a url is a local file returns a path suitable for passing to QFile. Otherwise returns an
+empty string.
+*/
+QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
+{
+ if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
+ if (url.length() > 4)
+ return QLatin1Char(':') + url.mid(4);
+ return QString();
+ }
+
+ return toLocalFile(url);
+}
+
+bool QQmlFile::bundleDirectoryExists(const QString &dir, QQmlEngine *e)
+{
+ if (!isBundle(dir))
+ return false;
+
+ int index = dir.indexOf(QLatin1Char('/'), 9);
+
+ if (index == -1 && dir.length() > 9) // We accept "bundle://<blah>" with no extra path
+ index = dir.length();
+
+ if (index == -1)
+ return false;
+
+ QStringRef identifier(&dir, 9, index - 9);
+ QStringRef path(&dir, index + 1, dir.length() - index - 1);
+
+ QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
+
+ if (bundle) {
+ int lastIndex = dir.lastIndexOf(QLatin1Char('/'));
+
+ if (lastIndex <= index) {
+ bundle->release();
+ return true;
+ }
+
+ QStringRef d(&dir, index + 1, lastIndex - index);
+
+ QList<const QQmlBundle::FileEntry *> entries = bundle->files();
+
+ for (int ii = 0; ii < entries.count(); ++ii) {
+ QString name = entries.at(ii)->fileName();
+ if (name.startsWith(d)) {
+ bundle->release();
+ return true;
+ }
+ }
+
+ bundle->release();
+ }
+
+ return false;
+}
+
+bool QQmlFile::bundleDirectoryExists(const QUrl &url, QQmlEngine *e)
+{
+ if (!isBundle(url))
+ return false;
+
+ QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
+
+ if (bundle) {
+ QString path = url.path();
+
+ int lastIndex = path.lastIndexOf(QLatin1Char('/'));
+
+ if (lastIndex == -1) {
+ bundle->release();
+ return true;
+ }
+
+ QStringRef d(&path, 0, lastIndex);
+
+ QList<const QQmlBundle::FileEntry *> entries = bundle->files();
+
+ for (int ii = 0; ii < entries.count(); ++ii) {
+ QString name = entries.at(ii)->fileName();
+ if (name.startsWith(d)) {
+ bundle->release();
+ return true;
+ }
+ }
+
+ bundle->release();
+ }
+
+ return false;
+}
+
+bool QQmlFile::bundleFileExists(const QString &file, QQmlEngine *e)
+{
+ if (!isBundle(file))
+ return false;
+
+ int index = file.indexOf(QLatin1Char('/'), 9);
+
+ if (index == -1)
+ return false;
+
+ QStringRef identifier(&file, 9, index - 9);
+ QStringRef path(&file, index + 1, file.length() - index - 1);
+
+ QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
+
+ if (bundle) {
+ const QQmlBundle::FileEntry *entry = bundle->find(path.constData(), path.length());
+ bundle->release();
+
+ return entry != 0;
+ }
+
+ return false;
+}
+
+bool QQmlFile::bundleFileExists(const QUrl &, QQmlEngine *)
+{
+ qFatal("Not implemented");
+ return true;
+}
+
+/*!
+Returns the file name for the bundle file referenced by \a url or an
+empty string if \a url isn't a bundle url.
+*/
+QString QQmlFile::bundleFileName(const QString &url, QQmlEngine *e)
+{
+ if (!isBundle(url))
+ return QString();
+
+ int index = url.indexOf(QLatin1Char('/'), 9);
+
+ if (index == -1)
+ index = url.length();
+
+ QStringRef identifier(&url, 9, index - 9);
+
+ QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(identifier);
+
+ if (bundle) {
+ QString rv = bundle->fileName;
+ bundle->release();
+ return rv;
+ }
+
+ return QString();
+}
+
+/*!
+Returns the file name for the bundle file referenced by \a url or an
+empty string if \a url isn't a bundle url.
+*/
+QString QQmlFile::bundleFileName(const QUrl &url, QQmlEngine *e)
+{
+ if (!isBundle(url))
+ return QString();
+
+ QQmlBundleData *bundle = QQmlEnginePrivate::get(e)->typeLoader.getBundle(url.host());
+
+ if (bundle) {
+ QString rv = bundle->fileName;
+ bundle->release();
+ return rv;
+ }
+
+ return QString();
+}
+
+QT_END_NAMESPACE
+
+#include "qqmlfile.moc"
diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h
new file mode 100644
index 0000000000..cfa833c6b7
--- /dev/null
+++ b/src/qml/qml/qqmlfile.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module 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$
+**
+****************************************************************************/
+
+#ifndef QQMLFILE_H
+#define QQMLFILE_H
+
+#include <QtQml/qtqmlglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QUrl;
+class QString;
+class QObject;
+class QQmlEngine;
+class QQmlFilePrivate;
+
+class Q_QML_EXPORT QQmlFile
+{
+public:
+ QQmlFile();
+ QQmlFile(QQmlEngine *, const QUrl &);
+ QQmlFile(QQmlEngine *, const QString &);
+ ~QQmlFile();
+
+ enum Status { Null, Ready, Error, Loading };
+
+ bool isNull() const;
+ bool isReady() const;
+ bool isError() const;
+ bool isLoading() const;
+
+ QUrl url() const;
+
+ Status status() const;
+ QString error() const;
+
+ qint64 size() const;
+ const char *data() const;
+ QByteArray dataByteArray() const;
+
+ QByteArray metaData(const QString &) const;
+
+ void load(QQmlEngine *, const QUrl &);
+ void load(QQmlEngine *, const QString &);
+
+ void clear();
+ void clear(QObject *);
+
+ bool connectFinished(QObject *, const char *);
+ bool connectFinished(QObject *, int);
+ bool connectDownloadProgress(QObject *, const char *);
+ bool connectDownloadProgress(QObject *, int);
+
+ static bool isSynchronous(const QString &url);
+ static bool isSynchronous(const QUrl &url);
+
+ static bool isBundle(const QString &url);
+ static bool isBundle(const QUrl &url);
+
+ static bool isLocalFile(const QString &url);
+ static bool isLocalFile(const QUrl &url);
+
+ static QString urlToLocalFileOrQrc(const QString &);
+ static QString urlToLocalFileOrQrc(const QUrl &);
+
+ static bool bundleDirectoryExists(const QString &, QQmlEngine *);
+ static bool bundleDirectoryExists(const QUrl &, QQmlEngine *);
+
+ static bool bundleFileExists(const QString &, QQmlEngine *);
+ static bool bundleFileExists(const QUrl &, QQmlEngine *);
+
+ static QString bundleFileName(const QString &, QQmlEngine *);
+ static QString bundleFileName(const QUrl &, QQmlEngine *);
+
+private:
+ Q_DISABLE_COPY(QQmlFile)
+ QQmlFilePrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQMLFILE_H
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index a8a3ed0a4b..1417e3dff7 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -43,6 +43,7 @@
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
+#include <QtQml/qqmlfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qpluginloader.h>
#include <QtCore/qlibraryinfo.h>
@@ -56,11 +57,6 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
-static bool greaterThan(const QString &s1, const QString &s2)
-{
- return s1 > s2;
-}
-
QString resolveLocalUrl(const QString &url, const QString &relative)
{
if (relative.contains(QLatin1Char(':'))) {
@@ -109,15 +105,13 @@ QString resolveLocalUrl(const QString &url, const QString &relative)
}
}
-
-
typedef QMap<QString, QString> StringStringMap;
Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
-class QQmlImportedNamespace
+class QQmlImportNamespace
{
public:
- struct Data {
+ struct Import {
QString uri;
QString url;
int majversion;
@@ -125,45 +119,58 @@ public:
bool isLibrary;
QQmlDirComponents qmlDirComponents;
QQmlDirScripts qmlDirScripts;
- };
- QList<Data> imports;
+ bool resolveType(QQmlTypeLoader *typeLoader, const QString& type,
+ int *vmajor, int *vminor,
+ QQmlType** type_return, QString* url_return,
+ QString *base = 0, bool *typeRecursionDetected = 0) const;
+ };
+ QList<Import> imports;
- bool find_helper(QQmlTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
- QQmlType** type_return, QString* url_return,
- QString *base = 0, bool *typeRecursionDetected = 0);
- bool find(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QQmlType** type_return,
- QString* url_return, QString *base = 0, QList<QQmlError> *errors = 0);
+ bool resolveType(QQmlTypeLoader *typeLoader, const QString& type,
+ int *vmajor, int *vminor,
+ QQmlType** type_return, QString* url_return,
+ QString *base = 0, QList<QQmlError> *errors = 0);
};
-class QQmlImportsPrivate {
+class QQmlImportsPrivate
+{
public:
QQmlImportsPrivate(QQmlTypeLoader *loader);
~QQmlImportsPrivate();
- bool importExtension(const QString &absoluteFilePath, const QString &uri,
- QQmlImportDatabase *database, QQmlDirComponents* components,
- QQmlDirScripts *scripts,
- QList<QQmlError> *errors);
-
- QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
- QString add(const QQmlDirComponents &qmldircomponentsnetwork,
- const QString& uri_arg, const QString& prefix,
- int vmaj, int vmin, QQmlScript::Import::Type importType,
- QQmlImportDatabase *database, QList<QQmlError> *errors);
- bool find(const QString& type, int *vmajor, int *vminor,
- QQmlType** type_return, QString* url_return, QList<QQmlError> *errors);
+ bool addImport(const QQmlDirComponents &qmldircomponentsnetwork,
+ const QString &importedUri, const QString& prefix,
+ int vmaj, int vmin, QQmlScript::Import::Type importType,
+ bool isImplicitImport, QQmlImportDatabase *database,
+ QString *, QList<QQmlError> *errors);
- QQmlImportedNamespace *findNamespace(const QString& type);
+ bool resolveType(const QString& type, int *vmajor, int *vminor,
+ QQmlType** type_return, QString* url_return,
+ QList<QQmlError> *errors);
QUrl baseUrl;
QString base;
int ref;
QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
- QQmlImportedNamespace unqualifiedset;
- QHash<QString,QQmlImportedNamespace* > set;
+ QQmlImportNamespace unqualifiedset;
+ QHash<QString, QQmlImportNamespace *> set;
QQmlTypeLoader *typeLoader;
+
+ static inline QString tr(const char *str) {
+ return QQmlImportDatabase::tr(str);
+ }
+
+private:
+ static bool locateQmldir(const QString &uri, int vmaj, int vmin,
+ QQmlImportDatabase *database,
+ QString *outQmldirFilePath, QString *outUrl);
+ bool importExtension(const QString &absoluteFilePath, const QString &uri,
+ QQmlImportDatabase *database, QQmlDirComponents* components,
+ QQmlDirScripts *scripts,
+ QString *url, QList<QQmlError> *errors);
+ QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database);
};
/*!
@@ -223,29 +230,30 @@ QUrl QQmlImports::baseUrl() const
void QQmlImports::populateCache(QQmlTypeNameCache *cache, QQmlEngine *engine) const
{
- const QQmlImportedNamespace &set = d->unqualifiedset;
+ const QQmlImportNamespace &set = d->unqualifiedset;
for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
- const QQmlImportedNamespace::Data &data = set.imports.at(ii);
- QQmlTypeModule *module = QQmlMetaType::typeModule(data.uri, data.majversion);
+ const QQmlImportNamespace::Import &import = set.imports.at(ii);
+ QQmlTypeModule *module = QQmlMetaType::typeModule(import.uri, import.majversion);
if (module)
- cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, data.minversion));
+ cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import.minversion));
}
- for (QHash<QString,QQmlImportedNamespace* >::ConstIterator iter = d->set.begin();
+ for (QHash<QString, QQmlImportNamespace* >::ConstIterator iter = d->set.begin();
iter != d->set.end();
++iter) {
- const QQmlImportedNamespace &set = *iter.value();
+ const QQmlImportNamespace &set = *iter.value();
for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
- const QQmlImportedNamespace::Data &data = set.imports.at(ii);
- QQmlTypeModule *module = QQmlMetaType::typeModule(data.uri, data.majversion);
+ const QQmlImportNamespace::Import &import = set.imports.at(ii);
+ QQmlTypeModule *module = QQmlMetaType::typeModule(import.uri, import.majversion);
if (module) {
- QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
- import.modules.append(QQmlTypeModuleVersion(module, data.minversion));
+ QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[iter.key()];
+ typeimport.modules.append(QQmlTypeModuleVersion(module, import.minversion));
}
- QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(data.uri, data.majversion, data.minversion);
+ QQmlMetaType::ModuleApi moduleApi = QQmlMetaType::moduleApi(import.uri, import.majversion,
+ import.minversion);
if (moduleApi.script || moduleApi.qobject) {
QQmlTypeNameCache::Import &import = cache->m_namedImports[iter.key()];
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
@@ -259,32 +267,32 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
{
QList<QQmlImports::ScriptReference> scripts;
- const QQmlImportedNamespace &set = d->unqualifiedset;
+ const QQmlImportNamespace &set = d->unqualifiedset;
for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
- const QQmlImportedNamespace::Data &data = set.imports.at(ii);
+ const QQmlImportNamespace::Import &import = set.imports.at(ii);
- foreach (const QQmlDirParser::Script &script, data.qmlDirScripts) {
+ foreach (const QQmlDirParser::Script &script, import.qmlDirScripts) {
ScriptReference ref;
ref.nameSpace = script.nameSpace;
- ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
+ ref.location = QUrl(import.url).resolved(QUrl(script.fileName));
scripts.append(ref);
}
}
- for (QHash<QString,QQmlImportedNamespace* >::ConstIterator iter = d->set.constBegin();
+ for (QHash<QString, QQmlImportNamespace* >::ConstIterator iter = d->set.constBegin();
iter != d->set.constEnd();
++iter) {
- const QQmlImportedNamespace &set = *iter.value();
+ const QQmlImportNamespace &set = *iter.value();
for (int ii = set.imports.count() - 1; ii >= 0; --ii) {
- const QQmlImportedNamespace::Data &data = set.imports.at(ii);
+ const QQmlImportNamespace::Import &import = set.imports.at(ii);
- foreach (const QQmlDirParser::Script &script, data.qmlDirScripts) {
+ foreach (const QQmlDirParser::Script &script, import.qmlDirScripts) {
ScriptReference ref;
ref.nameSpace = script.nameSpace;
ref.qualifier = iter.key();
- ref.location = QUrl(data.url).resolved(QUrl(script.fileName));
+ ref.location = QUrl(import.url).resolved(QUrl(script.fileName));
scripts.append(ref);
}
}
@@ -298,7 +306,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
The given (namespace qualified) \a type is resolved to either
\list
- \li a QQmlImportedNamespace stored at \a ns_return,
+ \li a QQmlImportNamespace stored at \a ns_return,
\li a QQmlType stored at \a type_return, or
\li a component located at \a url_return.
\endlist
@@ -308,27 +316,29 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const
\sa addImport()
*/
bool QQmlImports::resolveType(const QString& type,
- QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
- QQmlImportedNamespace** ns_return, QList<QQmlError> *errors) const
+ QQmlType** type_return, QString* url_return, int *vmaj, int *vmin,
+ QQmlImportNamespace** ns_return, QList<QQmlError> *errors) const
{
- QQmlImportedNamespace* ns = d->findNamespace(type);
+ QQmlImportNamespace* ns = d->set.value(type);
if (ns) {
if (ns_return)
*ns_return = ns;
return true;
}
if (type_return || url_return) {
- if (d->find(type,vmaj,vmin,type_return,url_return, errors)) {
+ if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) {
if (qmlImportTrace()) {
+#define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \
+ << ")" << "::resolveType: " << type << " => "
+
if (type_return && *type_return && url_return && !url_return->isEmpty())
- qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
- << type << " => " << (*type_return)->typeName() << " " << *url_return;
+ RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " " << *url_return;
if (type_return && *type_return)
- qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
- << type << " => " << (*type_return)->typeName();
+ RESOLVE_TYPE_DEBUG << (*type_return)->typeName();
if (url_return && !url_return->isEmpty())
- qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
- << type << " => " << *url_return;
+ RESOLVE_TYPE_DEBUG << *url_return;
+
+#undef RESOLVE_TYPE_DEBUG
}
return true;
}
@@ -346,42 +356,40 @@ bool QQmlImports::resolveType(const QString& type,
If either return pointer is 0, the corresponding search is not done.
*/
-bool QQmlImports::resolveType(QQmlImportedNamespace* ns, const QString& type,
- QQmlType** type_return, QString* url_return,
- int *vmaj, int *vmin) const
+bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QString& type,
+ QQmlType** type_return, QString* url_return,
+ int *vmaj, int *vmin) const
{
- return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
+ return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return);
}
-bool QQmlImportedNamespace::find_helper(QQmlTypeLoader *typeLoader, const Data &data, const QString& type, int *vmajor, int *vminor,
- QQmlType** type_return, QString* url_return,
- QString *base, bool *typeRecursionDetected)
+bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader,
+ const QString& type, int *vmajor, int *vminor,
+ QQmlType** type_return, QString* url_return,
+ QString *base, bool *typeRecursionDetected) const
{
- int vmaj = data.majversion;
- int vmin = data.minversion;
-
- if (vmaj >= 0 && vmin >= 0) {
- QString qt = data.uri + QLatin1Char('/') + type;
- QQmlType *t = QQmlMetaType::qmlType(qt,vmaj,vmin);
+ if (majversion >= 0 && minversion >= 0) {
+ QString qt = uri + QLatin1Char('/') + type;
+ QQmlType *t = QQmlMetaType::qmlType(qt, majversion, minversion);
if (t) {
- if (vmajor) *vmajor = vmaj;
- if (vminor) *vminor = vmin;
+ if (vmajor) *vmajor = majversion;
+ if (vminor) *vminor = minversion;
if (type_return)
*type_return = t;
return true;
}
}
- const QQmlDirComponents &qmldircomponents = data.qmlDirComponents;
bool typeWasDeclaredInQmldir = false;
- if (!qmldircomponents.isEmpty()) {
- foreach (const QQmlDirParser::Component &c, qmldircomponents) {
+ if (!qmlDirComponents.isEmpty()) {
+ foreach (const QQmlDirParser::Component &c, qmlDirComponents) {
if (c.typeName == type) {
typeWasDeclaredInQmldir = true;
// importing version -1 means import ALL versions
- if ((vmaj == -1) || (c.majorVersion == vmaj && vmin >= c.minorVersion)) {
- QString url(data.url + type + QLatin1String(".qml"));
- QString candidate = resolveLocalUrl(url, c.fileName);
+ if ((majversion == -1) || (c.majorVersion == majversion &&
+ minversion >= c.minorVersion)) {
+ QString candidate = resolveLocalUrl(QString(url + type + QLatin1String(".qml")),
+ c.fileName);
if (c.internal && base) {
if (resolveLocalUrl(*base, c.fileName) != candidate)
continue; // failed attempt to access an internal type
@@ -399,65 +407,205 @@ bool QQmlImportedNamespace::find_helper(QQmlTypeLoader *typeLoader, const Data &
}
}
- if (!typeWasDeclaredInQmldir && !data.isLibrary) {
- // XXX search non-files too! (eg. zip files, see QT-524)
- QString url(data.url + type + QLatin1String(".qml"));
- QString file = QQmlEnginePrivate::urlToLocalFileOrQrc(url);
- if (!typeLoader->absoluteFilePath(file).isEmpty()) {
- if (base && *base == url) { // no recursion
+ if (!typeWasDeclaredInQmldir && !isLibrary) {
+ QString qmlUrl(url + type + QLatin1String(".qml"));
+
+ bool exists = false;
+
+ if (QQmlFile::isBundle(qmlUrl)) {
+ exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine());
+ } else {
+ QString file = QQmlFile::urlToLocalFileOrQrc(qmlUrl);
+ exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty();
+ }
+
+ if (exists) {
+ if (base && *base == qmlUrl) { // no recursion
if (typeRecursionDetected)
*typeRecursionDetected = true;
} else {
if (url_return)
- *url_return = url;
+ *url_return = qmlUrl;
return true;
}
}
}
+
return false;
}
-QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
- : ref(1), typeLoader(loader)
+bool QQmlImportsPrivate::resolveType(const QString& type, int *vmajor, int *vminor,
+ QQmlType** type_return, QString* url_return,
+ QList<QQmlError> *errors)
+{
+ QQmlImportNamespace *s = 0;
+ int slash = type.indexOf(QLatin1Char('/'));
+ if (slash >= 0) {
+ QString namespaceName = type.left(slash);
+ s = set.value(namespaceName);
+ if (!s) {
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
+ errors->prepend(error);
+ }
+ return false;
+ }
+ int nslash = type.indexOf(QLatin1Char('/'),slash+1);
+ if (nslash > 0) {
+ if (errors) {
+ QQmlError error;
+ error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
+ errors->prepend(error);
+ }
+ return false;
+ }
+ } else {
+ s = &unqualifiedset;
+ }
+ QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
+ if (s) {
+ if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
+ return true;
+ if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
+ // qualified, and only 1 url
+ *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor,
+ int *vminor, QQmlType** type_return,
+ QString* url_return, QString *base, QList<QQmlError> *errors)
{
+ bool typeRecursionDetected = false;
+ for (int i=0; i<imports.count(); ++i) {
+ const Import &import = imports.at(i);
+ if (import.resolveType(typeLoader, type, vmajor, vminor, type_return, url_return,
+ base, &typeRecursionDetected)) {
+ if (qmlCheckTypes()) {
+ // check for type clashes
+ for (int j = i+1; j<imports.count(); ++j) {
+ const Import &import2 = imports.at(j);
+ if (import2.resolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) {
+ if (errors) {
+ QString u1 = imports.at(i).url;
+ QString u2 = imports.at(j).url;
+ if (base) {
+ QString b = *base;
+ int slash = b.lastIndexOf(QLatin1Char('/'));
+ if (slash >= 0) {
+ b = b.left(slash+1);
+ QString l = b.left(slash);
+ if (u1.startsWith(b))
+ u1 = u1.mid(b.count());
+ else if (u1 == l)
+ u1 = QQmlImportDatabase::tr("local directory");
+ if (u2.startsWith(b))
+ u2 = u2.mid(b.count());
+ else if (u2 == l)
+ u2 = QQmlImportDatabase::tr("local directory");
+ }
+ }
+
+ QQmlError error;
+ if (u1 != u2) {
+ error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
+ } else {
+ error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
+ .arg(u1)
+ .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
+ .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
+ }
+ errors->prepend(error);
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+ if (errors) {
+ QQmlError error;
+ if (typeRecursionDetected)
+ error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
+ else
+ error.setDescription(QQmlImportDatabase::tr("is not a type"));
+ errors->prepend(error);
+ }
+ return false;
+}
+
+QQmlImportsPrivate::QQmlImportsPrivate(QQmlTypeLoader *loader)
+: ref(1), typeLoader(loader) {
}
QQmlImportsPrivate::~QQmlImportsPrivate()
{
- foreach (QQmlImportedNamespace* s, set.values())
+ foreach (QQmlImportNamespace* s, set.values())
delete s;
}
-bool QQmlImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
- QQmlImportDatabase *database,
- QQmlDirComponents* components,
- QQmlDirScripts* scripts,
- QList<QQmlError> *errors)
+/*!
+Import an extension defined by a qmldir file.
+
+\a qmldirFilePath is either a raw file path, or a bundle url.
+
+This call will modify the \a url parameter if importing the extension redirects to
+a bundle path.
+*/
+bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
+ const QString &uri,
+ QQmlImportDatabase *database,
+ QQmlDirComponents* components,
+ QQmlDirScripts* scripts,
+ QString *url,
+ QList<QQmlError> *errors)
{
- const QQmlDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
+ // As qmldirFilePath is always local, this method can always return synchronously
+ const QQmlDirParser *qmldirParser = typeLoader->qmlDirParser(qmldirFilePath, uri, url);
if (qmldirParser->hasError()) {
if (errors) {
+ QUrl url;
+
+ if (QQmlFile::isBundle(qmldirFilePath))
+ url = QUrl(qmldirFilePath);
+ else
+ url = QUrl::fromLocalFile(qmldirFilePath);
+
const QList<QQmlError> qmldirErrors = qmldirParser->errors(uri);
- for (int i = 0; i < qmldirErrors.size(); ++i)
- errors->prepend(qmldirErrors.at(i));
+ for (int i = 0; i < qmldirErrors.size(); ++i) {
+ QQmlError error = qmldirErrors.at(i);
+ error.setUrl(url);
+ errors->append(error);
+ }
}
return false;
}
if (qmlImportTrace())
qDebug().nospace() << "QQmlImports(" << qPrintable(base) << "::importExtension: "
- << "loaded " << absoluteFilePath;
+ << "loaded " << qmldirFilePath;
+
+ if (!qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(qmldirFilePath)) {
+ qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
- if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
- qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
+ QString qmldirPath = qmldirFilePath;
- QString qmldirPath = absoluteFilePath;
- int slash = absoluteFilePath.lastIndexOf(QLatin1Char('/'));
+ if (QQmlFile::isBundle(*url))
+ qmldirPath = QQmlFile::bundleFileName(*url, typeLoader->engine());
+
+ int slash = qmldirFilePath.lastIndexOf(QLatin1Char('/'));
if (slash > 0)
qmldirPath.truncate(slash);
- foreach (const QQmlDirParser::Plugin &plugin, qmldirParser->plugins()) {
- QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name);
+ foreach (const QQmlDirParser::Plugin &plugin, qmldirParser->plugins()) {
+ QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
+ plugin.path, plugin.name);
if (!resolvedFilePath.isEmpty()) {
if (!database->importPlugin(resolvedFilePath, uri, errors)) {
if (errors) {
@@ -466,8 +614,8 @@ bool QQmlImportsPrivate::importExtension(const QString &absoluteFilePath, const
// The reason is that the lower level may add url and line/column numbering information.
QQmlError poppedError = errors->takeFirst();
QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
- error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
+ error.setDescription(tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(poppedError.description()));
+ error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
errors->prepend(error);
}
return false;
@@ -475,8 +623,8 @@ bool QQmlImportsPrivate::importExtension(const QString &absoluteFilePath, const
} else {
if (errors) {
QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
- error.setUrl(QUrl::fromLocalFile(absoluteFilePath));
+ error.setDescription(tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name));
+ error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
errors->prepend(error);
}
return false;
@@ -494,12 +642,16 @@ bool QQmlImportsPrivate::importExtension(const QString &absoluteFilePath, const
QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDatabase *database)
{
+ struct I { static bool greaterThan(const QString &s1, const QString &s2) {
+ return s1 > s2;
+ } };
+
QString dir = dir_arg;
if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
dir.chop(1);
QStringList paths = database->fileImportPath;
- qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
+ qSort(paths.begin(), paths.end(), I::greaterThan); // Ensure subdirs preceed their parents.
QString stableRelativePath = dir;
foreach(const QString &path, paths) {
@@ -520,208 +672,285 @@ QString QQmlImportsPrivate::resolvedUri(const QString &dir_arg, QQmlImportDataba
}
stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
+
return stableRelativePath;
}
-QString QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork,
- const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
- QQmlScript::Import::Type importType,
- QQmlImportDatabase *database, QList<QQmlError> *errors)
+/*
+Locates the qmldir file for \a uri version \a vmaj.vmin. Returns true if found,
+and fills in outQmldirFilePath and outQmldirUrl appropriately. Otherwise returns
+false.
+*/
+bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin,
+ QQmlImportDatabase *database,
+ QString *outQmldirFilePath,
+ QString *outQmldirPathUrl)
{
- static QLatin1String Slash_qmldir("/qmldir");
- static QLatin1Char Slash('/');
-
- QQmlDirComponents qmldircomponents = qmldircomponentsnetwork;
- QQmlDirScripts qmldirscripts;
- QString uri = uri_arg;
- QQmlImportedNamespace *s;
- if (prefix.isEmpty()) {
- s = &unqualifiedset;
- } else {
- s = set.value(prefix);
- if (!s)
- set.insert(prefix,(s=new QQmlImportedNamespace));
+ Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
+
+ // Check cache first
+
+ QQmlImportDatabase::QmldirCache *cacheHead = 0;
+ {
+ QQmlImportDatabase::QmldirCache **cachePtr = database->qmldirCache.value(uri);
+ if (cachePtr) {
+ cacheHead = *cachePtr;
+ QQmlImportDatabase::QmldirCache *cache = cacheHead;
+ while (cache) {
+ if (cache->versionMajor == vmaj && cache->versionMinor == vmin) {
+ *outQmldirFilePath = cache->qmldirFilePath;
+ *outQmldirPathUrl = cache->qmldirPathUrl;
+ return !cache->qmldirFilePath.isEmpty();
+ }
+ cache = cache->next;
+ }
+ }
}
- QString url = uri;
- bool versionFound = false;
- if (importType == QQmlScript::Import::Library) {
- Q_ASSERT(vmaj >= 0 && vmin >= 0); // Versions are always specified for libraries
+ static QLatin1Char Slash('/');
+ static QLatin1String Slash_qmldir("/qmldir");
- url.replace(QLatin1Char('.'), Slash);
- bool found = false;
- QString dir;
- QString qmldir;
+ QString url = uri;
+ url.replace(QLatin1Char('.'), Slash);
- // step 1: search for extension with fully encoded version number
+ // step 0: search for extension with fully encoded version number (eg. MyModule.3.2)
+ // step 1: search for extension with encoded version major (eg. MyModule.3)
+ // step 2: search for extension without version number (eg. MyModule)
+ for (int step = 0; step <= 2; ++step) {
foreach (const QString &p, database->fileImportPath) {
- dir = p+Slash+url;
-
- QFileInfo fi(dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir"));
- const QString absoluteFilePath = fi.absoluteFilePath();
+ QString dir = p + Slash + url;
- if (fi.isFile()) {
- found = true;
+ QString qmldirFile = dir;
+ if (step == 0) qmldirFile += QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
+ else if (step == 1) qmldirFile += QString(QLatin1String(".%1")).arg(vmaj);
+ qmldirFile += Slash_qmldir;
- const QString absolutePath = fi.absolutePath();
+ QQmlTypeLoader &typeLoader = QQmlEnginePrivate::get(database->engine)->typeLoader;
+ QString absoluteFilePath = typeLoader.absoluteFilePath(qmldirFile);
+ if (!absoluteFilePath.isEmpty()) {
+ QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
if (absolutePath.at(0) == QLatin1Char(':'))
url = QLatin1String("qrc://") + absolutePath.mid(1);
else
- url = QUrl::fromLocalFile(fi.absolutePath()).toString();
- uri = resolvedUri(dir, database);
- if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
- return QString();
- break;
+ url = QUrl::fromLocalFile(absolutePath).toString();
+
+ QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
+ cache->versionMajor = vmaj;
+ cache->versionMinor = vmin;
+ cache->qmldirFilePath = absoluteFilePath;
+ cache->qmldirPathUrl = url;
+ cache->next = cacheHead;
+ database->qmldirCache.insert(uri, cache);
+
+ *outQmldirFilePath = absoluteFilePath;
+ *outQmldirPathUrl = url;
+
+ return true;
}
}
+ }
- if (!found) {
- // step 2: search for extension with encoded version major
- foreach (const QString &p, database->fileImportPath) {
- dir = p+Slash+url;
-
- QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir"));
- const QString absoluteFilePath = fi.absoluteFilePath();
-
- if (fi.isFile()) {
- found = true;
-
- const QString absolutePath = fi.absolutePath();
- if (absolutePath.at(0) == QLatin1Char(':'))
- url = QLatin1String("qrc://") + absolutePath.mid(1);
- else
- url = QUrl::fromLocalFile(fi.absolutePath()).toString();
- uri = resolvedUri(dir, database);
- if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
- return QString();
- break;
- }
- }
+ QQmlImportDatabase::QmldirCache *cache = new QQmlImportDatabase::QmldirCache;
+ cache->versionMajor = vmaj;
+ cache->versionMinor = vmin;
+ cache->next = cacheHead;
+ database->qmldirCache.insert(uri, cache);
+
+ return false;
+}
+
+bool QQmlImportsPrivate::addImport(const QQmlDirComponents &qmldircomponentsnetwork,
+ const QString& importedUri, const QString& prefix,
+ int vmaj, int vmin, QQmlScript::Import::Type importType,
+ bool isImplicitImport, QQmlImportDatabase *database,
+ QString *outUrl, QList<QQmlError> *errors)
+{
+ Q_ASSERT(errors);
+ Q_ASSERT(importType == QQmlScript::Import::File || importType == QQmlScript::Import::Library);
+
+ static QLatin1String String_qmldir("qmldir");
+ static QLatin1String Slash_qmldir("/qmldir");
+ static QLatin1Char Slash('/');
+
+ // The list of components defined by a qmldir file for this import.
+ QQmlDirComponents qmldircomponents;
+ // The list of scripts defined by a qmldir file for this import.
+ QQmlDirScripts qmldirscripts;
+ // The namespace that this import affects.
+ QQmlImportNamespace *importSet = 0;
+ // The uri for this import. For library imports this is the same as importedUri
+ // specified by the user, but it may be different in the case of file imports.
+ QString uri;
+ // The url for the path containing files for this import.
+ QString url;
+
+ qmldircomponents = qmldircomponentsnetwork;
+ uri = importedUri;
+ if (prefix.isEmpty()) {
+ importSet = &unqualifiedset;
+ } else {
+ importSet = set.value(prefix);
+ if (!importSet) {
+ importSet = new QQmlImportNamespace;
+ set.insert(prefix, importSet);
}
+ }
+
+ if (importType == QQmlScript::Import::Library) {
+ Q_ASSERT(vmaj >= 0 && vmin >= 0);
+
+ QString qmldirFilePath;
+
+ if (locateQmldir(uri, vmaj, vmin, database, &qmldirFilePath, &url)) {
+
+ if (!importExtension(qmldirFilePath, uri, database, &qmldircomponents,
+ &qmldirscripts, &url, errors))
+ return false;
- if (!found) {
- // step 3: search for extension without version number
-
- foreach (const QString &p, database->fileImportPath) {
- dir = p+Slash+url;
- qmldir = dir+Slash_qmldir;
-
- QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
- if (!absoluteFilePath.isEmpty()) {
- found = true;
- QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(Slash)+1);
- if (absolutePath.at(0) == QLatin1Char(':'))
- url = QLatin1String("qrc://") + absolutePath.mid(1);
- else
- url = QUrl::fromLocalFile(absolutePath).toString();
- uri = resolvedUri(dir, database);
- if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors))
- return QString();
- break;
- }
- }
}
- if (QQmlMetaType::isModule(uri, vmaj, vmin))
- versionFound = true;
+ if (!QQmlMetaType::isModule(uri, vmaj, vmin)) {
- if (!versionFound && qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) {
- if (errors) {
- QQmlError error; // we don't set the url or line or column as these will be set by the loader.
+ if (qmldircomponents.isEmpty() && qmldirscripts.isEmpty()) {
+ QQmlError error;
if (QQmlMetaType::isAnyModule(uri))
- error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
+ error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(importedUri).arg(vmaj).arg(vmin));
else
- error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg));
+ error.setDescription(tr("module \"%1\" is not installed").arg(importedUri));
errors->prepend(error);
+ return false;
+ } else {
+ int lowest_min = INT_MAX;
+ int highest_min = INT_MIN;
+ typedef QList<QQmlDirParser::Component>::const_iterator ConstIterator;
+ typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator;
+
+ for (ConstIterator cit = qmldircomponents.constBegin();
+ cit != qmldircomponents.constEnd(); ++cit) {
+ if (cit->majorVersion == vmaj) {
+ lowest_min = qMin(lowest_min, cit->minorVersion);
+ highest_min = qMax(highest_min, cit->minorVersion);
+ }
+ }
+
+ for (SConstIterator cit = qmldirscripts.constBegin();
+ cit != qmldirscripts.constEnd(); ++cit) {
+ if (cit->majorVersion == vmaj) {
+ lowest_min = qMin(lowest_min, cit->minorVersion);
+ highest_min = qMax(highest_min, cit->minorVersion);
+ }
+ }
+
+ if (lowest_min > vmin || highest_min < vmin) {
+ QQmlError error;
+ error.setDescription(tr("module \"%1\" version %2.%3 is not installed").arg(importedUri).arg(vmaj).arg(vmin));
+ errors->prepend(error);
+ return false;
+ }
}
- return QString();
+
}
} else {
- if (importType == QQmlScript::Import::File && qmldircomponents.isEmpty()) {
- QString importUrl = resolveLocalUrl(base, uri + Slash_qmldir);
- QString localFileOrQrc = QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl);
- if (!localFileOrQrc.isEmpty()) {
- QString dir = QQmlEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
- if (!typeLoader->directoryExists(dir)) {
- if (errors) {
- QQmlError error; // we don't set the line or column as these will be set by the loader.
- error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri_arg));
- error.setUrl(QUrl(importUrl));
+
+ Q_ASSERT(importType == QQmlScript::Import::File);
+
+ if (qmldircomponents.isEmpty()) {
+
+ QString qmldirPath = uri;
+ if (uri.endsWith(Slash)) qmldirPath += String_qmldir;
+ else qmldirPath += Slash_qmldir;
+ QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
+
+ if (QQmlFile::isBundle(qmldirUrl)) {
+
+ QString dir = resolveLocalUrl(base, uri);
+ Q_ASSERT(QQmlFile::isBundle(dir));
+ if (!QQmlFile::bundleDirectoryExists(dir, typeLoader->engine())) {
+ if (!isImplicitImport) {
+ QQmlError error;
+ error.setDescription(tr("\"%1\": no such directory").arg(importedUri));
+ error.setUrl(QUrl(qmldirUrl));
errors->prepend(error);
}
- return QString(); // local import dirs must exist
+ return false;
}
+
+ // Transforms the (possible relative) uri into our best guess relative to the
+ // import paths.
uri = resolvedUri(dir, database);
+
if (uri.endsWith(Slash))
uri.chop(1);
- if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
- if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,&qmldirscripts,errors))
- return QString();
+ if (QQmlFile::bundleFileExists(qmldirUrl, typeLoader->engine())) {
+ if (!importExtension(qmldirUrl, uri, database, &qmldircomponents,
+ &qmldirscripts, &url, errors))
+ return false;
}
- } else {
- if (prefix.isEmpty()) {
- // directory must at least exist for valid import
- QString localFileOrQrc = QQmlEnginePrivate::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
- if (!typeLoader->directoryExists(localFileOrQrc)) {
- if (errors) {
- QQmlError error; // we don't set the line or column as these will be set by the loader.
- if (localFileOrQrc.isEmpty())
- error.setDescription(QQmlImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri));
- else
- error.setDescription(QQmlImportDatabase::tr("\"%1\": no such directory").arg(uri));
- error.setUrl(QUrl(importUrl));
- errors->prepend(error);
- }
- return QString();
+
+ } else if (QQmlFile::isLocalFile(qmldirUrl)) {
+
+ QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
+ Q_ASSERT(!localFileOrQrc.isEmpty());
+
+ QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, uri));
+ if (!typeLoader->directoryExists(dir)) {
+ if (!isImplicitImport) {
+ QQmlError error;
+ error.setDescription(tr("\"%1\": no such directory").arg(importedUri));
+ error.setUrl(QUrl(qmldirUrl));
+ errors->prepend(error);
}
+ return false;
}
- }
- }
- url = resolveLocalUrl(base, url);
- }
+ // Transforms the (possible relative) uri into our best guess relative to the
+ // import paths.
+ uri = resolvedUri(dir, database);
+
+ if (uri.endsWith(Slash))
+ uri.chop(1);
+ if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
+ if (!importExtension(localFileOrQrc, uri, database, &qmldircomponents,
+ &qmldirscripts, &url, errors))
+ return false;
+ }
- if (!versionFound && (vmaj > -1) && (vmin > -1) && !qmldircomponents.isEmpty()) {
- int lowest_min = INT_MAX;
- int highest_min = INT_MIN;
+ } else if (prefix.isEmpty()) {
- QList<QQmlDirParser::Component>::const_iterator cend = qmldircomponents.constEnd();
- for (QList<QQmlDirParser::Component>::const_iterator cit = qmldircomponents.constBegin(); cit != cend; ++cit) {
- if (cit->majorVersion == vmaj) {
- lowest_min = qMin(lowest_min, cit->minorVersion);
- highest_min = qMax(highest_min, cit->minorVersion);
- }
- }
+ if (!isImplicitImport) {
+ QQmlError error;
+ error.setDescription(tr("import \"%1\" has no qmldir and no namespace").arg(uri));
+ error.setUrl(QUrl(qmldirUrl));
+ errors->prepend(error);
+ }
+
+ return false;
- if (lowest_min > vmin || highest_min < vmin) {
- if (errors) {
- QQmlError error; // we don't set the url or line or column information, as these will be set by the loader.
- error.setDescription(QQmlImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin));
- errors->prepend(error);
}
- return QString();
}
+
+ url = resolveLocalUrl(base, importedUri);
+ if (!url.endsWith(Slash))
+ url += Slash;
}
- if (!url.endsWith(Slash))
- url += Slash;
+ Q_ASSERT(url.isEmpty() || url.endsWith(Slash));
QMap<QString, QQmlDirParser::Script> scripts;
-
if (!qmldirscripts.isEmpty()) {
// Verify that we haven't imported these scripts already
- QList<QQmlImportedNamespace::Data>::const_iterator end = s->imports.constEnd();
- for (QList<QQmlImportedNamespace::Data>::const_iterator it = s->imports.constBegin(); it != end; ++it) {
+ for (QList<QQmlImportNamespace::Import>::const_iterator it = importSet->imports.constBegin();
+ it != importSet->imports.constEnd(); ++it) {
if (it->uri == uri) {
QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
+ error.setDescription(tr("\"%1\" is ambiguous. Found in %2 and in %3").arg(uri).arg(url).arg(it->url));
errors->prepend(error);
- return QString();
+ return false;
}
}
- QList<QQmlDirParser::Script>::const_iterator send = qmldirscripts.constEnd();
- for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin(); sit != send; ++sit) {
+ for (QList<QQmlDirParser::Script>::const_iterator sit = qmldirscripts.constBegin();
+ sit != qmldirscripts.constEnd(); ++sit) {
// Only include scripts that match our requested version
if (((vmaj == -1) || (sit->majorVersion == vmaj)) &&
((vmin == -1) || (sit->minorVersion <= vmin))) {
@@ -735,124 +964,80 @@ QString QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork
}
}
- QQmlImportedNamespace::Data data;
- data.uri = uri;
- data.url = url;
- data.majversion = vmaj;
- data.minversion = vmin;
- data.isLibrary = importType == QQmlScript::Import::Library;
- data.qmlDirComponents = qmldircomponents;
- data.qmlDirScripts = scripts.values();
+ QQmlImportNamespace::Import import;
+ import.uri = uri;
+ import.url = url;
+ import.majversion = vmaj;
+ import.minversion = vmin;
+ import.isLibrary = importType == QQmlScript::Import::Library;
+ import.qmlDirComponents = qmldircomponents;
+ import.qmlDirScripts = scripts.values();
+
+ importSet->imports.prepend(import);
- s->imports.prepend(data);
+ if (outUrl) *outUrl = url;
- return data.url;
+ return true;
}
-bool QQmlImportsPrivate::find(const QString& type, int *vmajor, int *vminor, QQmlType** type_return,
- QString* url_return, QList<QQmlError> *errors)
+/*!
+ \internal
+
+ Adds an implicit "." file import. This is equivalent to calling addImport(), but error
+ messages related to the path or qmldir file not existing are suppressed.
+*/
+bool QQmlImports::addImplicitImport(QQmlImportDatabase *importDb,
+ const QQmlDirComponents &qmldircomponentsnetwork,
+ QList<QQmlError> *errors)
{
- QQmlImportedNamespace *s = 0;
- int slash = type.indexOf(QLatin1Char('/'));
- if (slash >= 0) {
- QString namespaceName = type.left(slash);
- s = set.value(namespaceName);
- if (!s) {
- if (errors) {
- QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("- %1 is not a namespace").arg(namespaceName));
- errors->prepend(error);
- }
- return false;
- }
- int nslash = type.indexOf(QLatin1Char('/'),slash+1);
- if (nslash > 0) {
- if (errors) {
- QQmlError error;
- error.setDescription(QQmlImportDatabase::tr("- nested namespaces not allowed"));
- errors->prepend(error);
- }
- return false;
- }
- } else {
- s = &unqualifiedset;
- }
- QString unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
- if (s) {
- if (s->find(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors))
- return true;
- if (s->imports.count() == 1 && !s->imports.at(0).isLibrary && url_return && s != &unqualifiedset) {
- // qualified, and only 1 url
- *url_return = resolveLocalUrl(s->imports.at(0).url, unqualifiedtype + QLatin1String(".qml"));
- return true;
- }
- }
+ Q_ASSERT(errors);
- return false;
-}
+ if (qmlImportTrace())
+ qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString())
+ << ")::addImplicitImport";
-QQmlImportedNamespace *QQmlImportsPrivate::findNamespace(const QString& type)
-{
- return set.value(type);
+
+ return d->addImport(qmldircomponentsnetwork, QLatin1String("."), QString(), -1, -1,
+ QQmlScript::Import::File, true, importDb, 0, errors);
}
-bool QQmlImportedNamespace::find(QQmlTypeLoader *typeLoader, const QString& type, int *vmajor, int *vminor, QQmlType** type_return,
- QString* url_return, QString *base, QList<QQmlError> *errors)
+/*!
+ \internal
+
+ Adds information to \a imports such that subsequent calls to resolveType()
+ will resolve types qualified by \a prefix by considering types found at the given \a uri.
+
+ The uri is either a directory (if importType is FileImport), or a URI resolved using paths
+ added via addImportPath() (if importType is LibraryImport).
+
+ The \a prefix may be empty, in which case the import location is considered for
+ unqualified types.
+
+ The base URL must already have been set with Import::setBaseUrl().
+
+ Optionally, the url the import resolved to can be returned by providing the url parameter.
+ Not all imports will result in an output url being generated, in which case the url will
+ be set to an empty string.
+
+ Returns true on success, and false on failure. In case of failure, the errors array will
+ filled appropriately.
+*/
+bool QQmlImports::addImport(QQmlImportDatabase *importDb,
+ const QString& uri, const QString& prefix, int vmaj, int vmin,
+ QQmlScript::Import::Type importType,
+ const QQmlDirComponents &qmldircomponentsnetwork,
+ QString *url, QList<QQmlError> *errors)
{
- bool typeRecursionDetected = false;
- for (int i=0; i<imports.count(); ++i) {
- if (find_helper(typeLoader, imports.at(i), type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
- if (qmlCheckTypes()) {
- // check for type clashes
- for (int j = i+1; j<imports.count(); ++j) {
- if (find_helper(typeLoader, imports.at(j), type, vmajor, vminor, 0, 0, base)) {
- if (errors) {
- QString u1 = imports.at(i).url;
- QString u2 = imports.at(j).url;
- if (base) {
- QString b = *base;
- int slash = b.lastIndexOf(QLatin1Char('/'));
- if (slash >= 0) {
- b = b.left(slash+1);
- QString l = b.left(slash);
- if (u1.startsWith(b))
- u1 = u1.mid(b.count());
- else if (u1 == l)
- u1 = QQmlImportDatabase::tr("local directory");
- if (u2.startsWith(b))
- u2 = u2.mid(b.count());
- else if (u2 == l)
- u2 = QQmlImportDatabase::tr("local directory");
- }
- }
+ Q_ASSERT(errors);
- QQmlError error;
- if (u1 != u2) {
- error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 and in %2").arg(u1).arg(u2));
- } else {
- error.setDescription(QQmlImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
- .arg(u1)
- .arg(imports.at(i).majversion).arg(imports.at(i).minversion)
- .arg(imports.at(j).majversion).arg(imports.at(j).minversion));
- }
- errors->prepend(error);
- }
- return false;
- }
- }
- }
- return true;
- }
- }
- if (errors) {
- QQmlError error;
- if (typeRecursionDetected)
- error.setDescription(QQmlImportDatabase::tr("is instantiated recursively"));
- else
- error.setDescription(QQmlImportDatabase::tr("is not a type"));
- errors->prepend(error);
- }
- return false;
+ if (qmlImportTrace())
+ qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
+ << uri << " " << vmaj << '.' << vmin << " "
+ << (importType==QQmlScript::Import::Library? "Library" : "File")
+ << " as " << prefix;
+
+ return d->addImport(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, false,
+ importDb, url, errors);
}
/*!
@@ -890,37 +1075,16 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
QQmlImportDatabase::~QQmlImportDatabase()
{
-}
-
-/*!
- \internal
-
- Adds information to \a imports such that subsequent calls to resolveType()
- will resolve types qualified by \a prefix by considering types found at the given \a uri.
-
- The uri is either a directory (if importType is FileImport), or a URI resolved using paths
- added via addImportPath() (if importType is LibraryImport).
-
- The \a prefix may be empty, in which case the import location is considered for
- unqualified types.
-
- Returns the resolved URL of the import on success.
-
- The base URL must already have been set with Import::setBaseUrl().
-*/
-QString QQmlImports::addImport(QQmlImportDatabase *importDb,
- const QString& uri, const QString& prefix, int vmaj, int vmin,
- QQmlScript::Import::Type importType,
- const QQmlDirComponents &qmldircomponentsnetwork,
- QList<QQmlError> *errors)
-{
- if (qmlImportTrace())
- qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
- << uri << " " << vmaj << '.' << vmin << " "
- << (importType==QQmlScript::Import::Library? "Library" : "File")
- << " as " << prefix;
-
- return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errors);
+ for (QStringHash<QmldirCache *>::ConstIterator iter = qmldirCache.begin();
+ iter != qmldirCache.end(); ++iter) {
+
+ QmldirCache *c = *iter;
+ while (c) {
+ QmldirCache *n = c->next;
+ delete c;
+ c = n;
+ }
+ }
}
/*!
@@ -932,9 +1096,10 @@ QString QQmlImports::addImport(QQmlImportDatabase *importDb,
\a qmldirPath is the location of the qmldir file.
*/
QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
- const QString &qmldirPath, const QString &qmldirPluginPath,
- const QString &baseName, const QStringList &suffixes,
- const QString &prefix)
+ const QString &qmldirPath,
+ const QString &qmldirPluginPath,
+ const QString &baseName, const QStringList &suffixes,
+ const QString &prefix)
{
QStringList searchPaths = filePluginPath;
bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 422f2429a0..d673b64766 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE
class QQmlTypeNameCache;
class QQmlEngine;
class QDir;
-class QQmlImportedNamespace;
+class QQmlImportNamespace;
class QQmlImportsPrivate;
class QQmlImportDatabase;
class QQmlTypeLoader;
@@ -86,18 +86,22 @@ public:
bool resolveType(const QString& type,
QQmlType** type_return, QString* url_return,
int *version_major, int *version_minor,
- QQmlImportedNamespace** ns_return,
+ QQmlImportNamespace** ns_return,
QList<QQmlError> *errors = 0) const;
- bool resolveType(QQmlImportedNamespace*,
+ bool resolveType(QQmlImportNamespace*,
const QString& type,
QQmlType** type_return, QString* url_return,
int *version_major, int *version_minor) const;
- QString addImport(QQmlImportDatabase *,
+ bool addImplicitImport(QQmlImportDatabase *importDb,
+ const QQmlDirComponents &qmldircomponentsnetwork,
+ QList<QQmlError> *errors);
+
+ bool addImport(QQmlImportDatabase *,
const QString& uri, const QString& prefix, int vmaj, int vmin,
QQmlScript::Import::Type importType,
const QQmlDirComponents &qmldircomponentsnetwork,
- QList<QQmlError> *errors);
+ QString *url, QList<QQmlError> *errors);
void populateCache(QQmlTypeNameCache *cache, QQmlEngine *) const;
@@ -142,6 +146,16 @@ private:
const QString &qmldirPath, const QString &qmldirPluginPath,
const QString &baseName);
+ struct QmldirCache {
+ int versionMajor;
+ int versionMinor;
+ QString qmldirFilePath;
+ QString qmldirPathUrl;
+ QmldirCache *next;
+ };
+ // Maps from an import to a linked list of qmldir info.
+ // Used in QQmlImportsPrivate::locateQmldir()
+ QStringHash<QmldirCache *> qmldirCache;
// XXX thread
QStringList filePluginPath;
diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp
index 05882f313d..b1a50a4f9a 100644
--- a/src/qml/qml/qqmlscript.cpp
+++ b/src/qml/qml/qqmlscript.cpp
@@ -451,22 +451,6 @@ QStringList QQmlScript::Variant::asStringList() const
//
// Actual parser classes
//
-void QQmlScript::Import::extractVersion(int *maj, int *min) const
-{
- *maj = -1; *min = -1;
-
- if (!version.isEmpty()) {
- int dot = version.indexOf(QLatin1Char('.'));
- if (dot < 0) {
- *maj = version.toInt();
- *min = 0;
- } else {
- *maj = version.left(dot).toInt();
- *min = version.mid(dot+1).toInt();
- }
- }
-}
-
namespace {
class ProcessAST: protected AST::Visitor
@@ -526,6 +510,8 @@ public:
void operator()(const QString &code, AST::Node *node);
+ static void extractVersion(QStringRef string, int *maj, int *min);
+
protected:
QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
@@ -661,6 +647,26 @@ QString ProcessAST::qualifiedNameId() const
return _scope.join(QLatin1String("/"));
}
+void ProcessAST::extractVersion(QStringRef string, int *maj, int *min)
+{
+ *maj = -1; *min = -1;
+
+ if (!string.isEmpty()) {
+
+ int dot = string.indexOf(QLatin1Char('.'));
+
+ if (dot < 0) {
+ *maj = string.toString().toInt();
+ *min = 0;
+ } else {
+ const QString *s = string.string();
+ int p = string.position();
+ *maj = QStringRef(s, p, dot).toString().toInt();
+ *min = QStringRef(s, p + dot + 1, string.size() - dot - 1).toString().toInt();
+ }
+ }
+}
+
QString ProcessAST::asString(AST::UiQualifiedId *node) const
{
QString s;
@@ -898,7 +904,7 @@ bool ProcessAST::visit(AST::UiImport *node)
}
if (node->versionToken.isValid()) {
- import.version = textAt(node->versionToken);
+ extractVersion(textRefAt(node->versionToken), &import.majorVersion, &import.minorVersion);
} else if (import.type == QQmlScript::Import::Library) {
QQmlError error;
error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version"));
@@ -1290,8 +1296,13 @@ public:
};
}
-bool QQmlScript::Parser::parse(const QByteArray &qmldata, const QUrl &url,
- const QString &urlString)
+QByteArray QQmlScript::Parser::preparseData() const
+{
+ return QByteArray();
+}
+
+bool QQmlScript::Parser::parse(const QString &qmlcode, const QByteArray &preparseData,
+ const QUrl &url, const QString &urlString)
{
clear();
@@ -1302,11 +1313,7 @@ bool QQmlScript::Parser::parse(const QByteArray &qmldata, const QUrl &url,
_scriptFile = urlString;
}
- QTextStream stream(qmldata, QIODevice::ReadOnly);
-#ifndef QT_NO_TEXTCODEC
- stream.setCodec("UTF-8");
-#endif
- QString *code = _pool.NewString(stream.readAll());
+ QString *code = _pool.NewString(qmlcode);
data = new QQmlScript::ParserJsASTData(_scriptFile);
@@ -1573,7 +1580,6 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri
} else {
// URI
QString uri;
- QString version;
while (true) {
if (!isUriToken(token))
@@ -1593,7 +1599,9 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri
}
CHECK_TOKEN(T_NUMERIC_LITERAL);
- version = script.mid(l.tokenOffset(), l.tokenLength());
+ int vmaj, vmin;
+ ProcessAST::extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()),
+ &vmaj, &vmin);
token = l.lex();
@@ -1624,7 +1632,8 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri
Import import;
import.type = Import::Library;
import.uri = uri;
- import.version = version;
+ import.majorVersion = vmaj;
+ import.minorVersion = vmin;
import.qualifier = importId;
import.location = location;
diff --git a/src/qml/qml/qqmlscript_p.h b/src/qml/qml/qqmlscript_p.h
index 8705f2aef9..1e46e6b655 100644
--- a/src/qml/qml/qqmlscript_p.h
+++ b/src/qml/qml/qqmlscript_p.h
@@ -109,16 +109,16 @@ struct LocationSpan
class Import
{
public:
- Import() : type(Library) {}
+ Import() : type(Library), majorVersion(-1), minorVersion(-1) {}
enum Type { Library, File, Script };
Type type;
QString uri;
QString qualifier;
- QString version;
- void extractVersion(int *maj, int *min) const;
+ int majorVersion;
+ int minorVersion;
QQmlScript::LocationSpan location;
};
@@ -469,14 +469,16 @@ public:
};
class ParserJsASTData;
-class Q_AUTOTEST_EXPORT Parser
+class Q_QML_EXPORT Parser
{
public:
Parser();
~Parser();
- bool parse(const QByteArray &data, const QUrl &url = QUrl(),
- const QString &urlString = QString());
+ bool parse(const QString &data, const QByteArray &preparseData,
+ const QUrl &url = QUrl(), const QString &urlString = QString());
+
+ QByteArray preparseData() const;
QList<TypeReference*> referencedTypes() const;
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();
}
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 319b2e7a10..66f4fd5081 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -58,6 +58,7 @@
#include <QtNetwork/qnetworkreply.h>
#include <QtQml/qqmlerror.h>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlfile.h>
#include <private/qv8_p.h>
#include <private/qhashedstring_p.h>
@@ -65,6 +66,8 @@
#include <private/qqmlimport_p.h>
#include <private/qqmlcleanup_p.h>
#include <private/qqmldirparser_p.h>
+#include <private/qqmlbundle_p.h>
+#include <private/qflagpointer_p.h>
QT_BEGIN_NAMESPACE
@@ -117,6 +120,25 @@ public:
QList<QQmlError> errors() const;
+ class Data {
+ public:
+ inline const char *data() const;
+ inline int size() const;
+
+ inline QByteArray asByteArray() const;
+
+ inline bool isFile() const;
+ inline QQmlFile *asFile() const;
+
+ private:
+ friend class QQmlDataBlob;
+ friend class QQmlDataLoader;
+ inline Data();
+ Data(const Data &);
+ Data &operator=(const Data &);
+ QBiPointer<const QByteArray, QQmlFile> d;
+ };
+
protected:
// Can be called from within callbacks
void setError(const QQmlError &);
@@ -124,7 +146,7 @@ protected:
void addDependency(QQmlDataBlob *);
// Callbacks made in load thread
- virtual void dataReceived(const QByteArray &) = 0;
+ virtual void dataReceived(const Data &) = 0;
virtual void done();
virtual void networkError(QNetworkReply::NetworkError);
virtual void dependencyError(QQmlDataBlob *);
@@ -216,12 +238,22 @@ private:
typedef QHash<QNetworkReply *, QQmlDataBlob *> NetworkReplies;
void setData(QQmlDataBlob *, const QByteArray &);
+ void setData(QQmlDataBlob *, QQmlFile *);
+ void setData(QQmlDataBlob *, const QQmlDataBlob::Data &);
QQmlEngine *m_engine;
QQmlDataLoaderThread *m_thread;
NetworkReplies m_networkReplies;
};
+class QQmlBundleData : public QQmlBundle,
+ public QQmlRefCount
+{
+public:
+ QQmlBundleData(const QString &);
+ QString fileName;
+};
+
// Exported for QtQuick1
class Q_QML_PRIVATE_EXPORT QQmlTypeLoader : public QQmlDataLoader
{
@@ -243,22 +275,36 @@ public:
QQmlScriptBlob *getScript(const QUrl &);
QQmlQmldirData *getQmldir(const QUrl &);
+ QQmlBundleData *getBundle(const QString &);
+ QQmlBundleData *getBundle(const QHashedStringRef &);
+ void addBundle(const QString &, const QString &);
+
QString absoluteFilePath(const QString &path);
bool directoryExists(const QString &path);
- const QQmlDirParser *qmlDirParser(const QString &absoluteFilePath);
+ const QQmlDirParser *qmlDirParser(const QString &filePath, const QString &uriHint, QString *outUrl);
+
private:
+ void addBundleNoLock(const QString &, const QString &);
+ QString bundleIdForQmldir(const QString &qmldir, const QString &uriHint);
+
+ struct DirParser : public QQmlDirParser { QString adjustedUrl; };
+
typedef QHash<QUrl, QQmlTypeData *> TypeCache;
typedef QHash<QUrl, QQmlScriptBlob *> ScriptCache;
typedef QHash<QUrl, QQmlQmldirData *> QmldirCache;
typedef QStringHash<bool> StringSet;
typedef QStringHash<StringSet*> ImportDirCache;
- typedef QStringHash<QQmlDirParser*> ImportQmlDirCache;
+ typedef QStringHash<DirParser*> ImportQmlDirCache;
+ typedef QStringHash<QQmlBundleData *> BundleCache;
+ typedef QStringHash<QString> QmldirBundleIdCache;
TypeCache m_typeCache;
ScriptCache m_scriptCache;
QmldirCache m_qmldirCache;
ImportDirCache m_importDirCache;
ImportQmlDirCache m_importQmlDirCache;
+ BundleCache m_bundleCache;
+ QmldirBundleIdCache m_qmldirBundleIdCache;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlTypeLoader::Options)
@@ -312,7 +358,7 @@ public:
protected:
virtual void done();
virtual void completed();
- virtual void dataReceived(const QByteArray &);
+ virtual void dataReceived(const Data &);
virtual void allDependenciesDone();
virtual void downloadProgressChanged(qreal);
@@ -349,8 +395,7 @@ private:
// reference that was created is released but final deletion only occurs once all the
// references as released. This is all intended to ensure that the v8 resources are
// only created and destroyed in the main thread :)
-class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup,
- public QQmlRefCount
+class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount
{
public:
QQmlScriptData();
@@ -402,7 +447,7 @@ public:
QQmlScriptData *scriptData() const;
protected:
- virtual void dataReceived(const QByteArray &);
+ virtual void dataReceived(const Data &);
virtual void done();
private:
@@ -424,13 +469,52 @@ public:
const QQmlDirComponents &dirComponents() const;
protected:
- virtual void dataReceived(const QByteArray &);
+ virtual void dataReceived(const Data &);
private:
QQmlDirComponents m_components;
};
+QQmlDataBlob::Data::Data()
+{
+}
+
+const char *QQmlDataBlob::Data::data() const
+{
+ Q_ASSERT(!d.isNull());
+
+ if (d.isT1()) return d.asT1()->constData();
+ else return d.asT2()->data();
+}
+
+int QQmlDataBlob::Data::size() const
+{
+ Q_ASSERT(!d.isNull());
+
+ if (d.isT1()) return d.asT1()->size();
+ else return d.asT2()->size();
+}
+
+bool QQmlDataBlob::Data::isFile() const
+{
+ return d.isT2();
+}
+
+QByteArray QQmlDataBlob::Data::asByteArray() const
+{
+ Q_ASSERT(!d.isNull());
+
+ if (d.isT1()) return *d.asT1();
+ else return d.asT2()->dataByteArray();
+}
+
+QQmlFile *QQmlDataBlob::Data::asFile() const
+{
+ if (d.isT2()) return d.asT2();
+ else return 0;
+}
+
QT_END_NAMESPACE
#endif // QQMLTYPELOADER_P_H
diff --git a/src/qml/qml/qquickworkerscript.cpp b/src/qml/qml/qquickworkerscript.cpp
index b00847abcb..b0d39fd0f8 100644
--- a/src/qml/qml/qquickworkerscript.cpp
+++ b/src/qml/qml/qquickworkerscript.cpp
@@ -55,6 +55,7 @@
#include <QtCore/qdatetime.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlfile.h>
#include "qqmlnetworkaccessmanagerfactory.h"
#include <private/qv8engine_p.h>
@@ -362,7 +363,7 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
if (url.isRelative())
return;
- QString fileName = QQmlEnginePrivate::urlToLocalFileOrQrc(url);
+ QString fileName = QQmlFile::urlToLocalFileOrQrc(url);
QFile f(fileName);
if (f.open(QIODevice::ReadOnly)) {
diff --git a/src/qml/qml/v8/qv8include.cpp b/src/qml/qml/v8/qv8include.cpp
index 89f60f256e..01a670aab7 100644
--- a/src/qml/qml/v8/qv8include.cpp
+++ b/src/qml/qml/v8/qv8include.cpp
@@ -45,6 +45,7 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtCore/qfile.h>
+#include <QtQml/qqmlfile.h>
#include <private/qqmlengine_p.h>
@@ -185,7 +186,7 @@ v8::Handle<v8::Value> QV8Include::include(const v8::Arguments &args)
if (args.Length() >= 2 && args[1]->IsFunction())
callbackFunction = v8::Local<v8::Function>::Cast(args[1]);
- QString localFile = QQmlEnginePrivate::urlToLocalFileOrQrc(url);
+ QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
v8::Local<v8::Object> result;