diff options
9 files changed, 248 insertions, 76 deletions
diff --git a/doc/src/declarative/modules.qdoc b/doc/src/declarative/modules.qdoc index d8797ea..f1ebd00 100644 --- a/doc/src/declarative/modules.qdoc +++ b/doc/src/declarative/modules.qdoc @@ -44,7 +44,7 @@ example, an \c import statement is required to use: \list \o A component defined in another QML file that is not in the same directory \o A component defined in a QML file located on a remote server -\o A \l{QDeclarativeExtensionPlugin}{QML C++ plugin} library (unless the plugin is installed in the same directory) +\o A \l{QDeclarativeExtensionPlugin}{QML extension plugin} library (unless the plugin is installed in the same directory) \o A JavaScript file (note this must be imported using \l {#namespaces}{named imports}) \endlist @@ -61,12 +61,12 @@ are not included in the global namespace by default.) The \c Qt module is an \e installed module; it is found in the \l{The QML import path}{import path}. There are two types of QML modules: -location modules (defined by a URL) and installed modules (defined by a URI). +located modules (defined by a URL) and installed modules (defined by a URI). -\section1 Location Modules +\section1 Located Modules -Location modules can reside on the local filesystem or a network resource, +Located modules can reside on the local filesystem or a network resource, and are referred to by a quoted location URL that specifies the filesystem or network URL. They allow any directory with QML content to be imported as a module, whether the directory is on the local filesystem or a remote @@ -86,8 +86,9 @@ directory using a relative or absolute path, like this: \code MyQMLProject |- MyComponents - |- Slider.qml |- CheckBox.qml + |- Slider.qml + |- Window.qml |- Main |- application.qml \endcode @@ -96,8 +97,10 @@ MyQMLProject \code import "../MyComponents" -Slider { ... } -CheckBox { ... } +Window { + Slider { ... } + CheckBox { ... } +} \endcode \endtable @@ -106,23 +109,51 @@ Similarly, if the directory resided on a network source, it could be imported like this: \code - import "https://qml.nokia.com/qml/qmlcomponents" - import "https://qml.nokia.com/qml/qmlcomponents" 1.0 + import "http://www.my-server.com/MyQMLProject/MyComponents" + import "http://www.my-server.com/MyQMLProject/MyComponents" 1.0 \endcode -Remote location modules must have a \l{Writing a qmldir file}{qmldir file} in the -same directory to specify which QML files should be made available. See the -\l {#qmldirexample}{example} below. The qmldir file is optional for modules on -the local filesystem. +A located module can also be imported as a network resource if it has a +\l{Writing a qmldir file}{qmldir file} in the directory that specifies the QML files +to be made available by the module. For example, if the \c MyComponents directory +contained a \c qmldir file defined like this: +\code +Slider 1.0 Slider.qml +CheckBox 1.0 CheckBox.qml +Window 1.0 Window.qml +\endcode + +If the \c MyComponents directory was then hosted as a network resource, it could +be imported as a module, like this: + +\code +import "http://the-server-name.com/MyQMLProject/MyComponents" + +Window { + Slider { ... } + CheckBox { ... } +} +\endcode + +with an optional "1.0" version specification. Notice the import would fail if +a later version was used, as the \c qmldir file specifies that these elements +are only available in the 1.0 version. + +Note that modules imported as a network resource allow only access to components +defined in QML files; components defined by C++ \l{QDeclarativeExtensionPlugin}{QML extension plugins} +are not available. \section1 Installed modules +Installed modules are modules that are made available through the QML import path, +as defined by QDeclarativeEngine::importPathList(), or modules defined within +C++ application code. An installed module is referred to by a URI, which allows +the module to be imported from QML code without specifying a complete filesystem +path or network resource URL. -Installed modules are modules that are installed on the -local filesystem within the QML import path, or modules defined in C++ -application code. When importing an installed module, an un-quoted URI is +When importing an installed module, an un-quoted URI is used, with a mandatory version number: \code @@ -130,9 +161,10 @@ used, with a mandatory version number: import com.nokia.qml.mymodule 1.0 \endcode -Installed modules that are installed into the import path or created -as a \l{QDeclarativeExtensionPlugin}{QML C++ plugin} must define a -\l{Writing a qmldir file}{qmldir file}. +When a module is imported, the QML engine searches the QML import path for a matching +module. The root directory of the module must contain a +\l{Writing a qmldir file}{qmldir file} that defines the QML files +and/or C++ QML extension plugins that are made available to the module. Modules that are installed into the import path translate the URI into directory names. For example, the qmldir file of the module \c com.nokia.qml.mymodule @@ -143,11 +175,9 @@ module could be located under \c com/nokia/qml/mymodule.2/qmldir or \c com/nokia/qml/mymodule.2.1/qmldir. The engine will automatically load the module which matches best. - -\section2 The QML import path - -The QML engine will search the import path for a requested installed module. -The default import path includes: +The import path, as returned by QDeclarativeEngine::importPathList(), defines the default +locations to be searched by the QML engine for a matching module. By default, this list +contains: \list \o The directory of the current file @@ -155,30 +185,80 @@ The default import path includes: \o Paths specified by the \c QML_IMPORT_PATH environment variable \endlist -The import path can be queried using QDeclarativeEngine::importPathList() and modified using QDeclarativeEngine::addImportPath(). +Additional import paths can be added through QDeclarativeEngine::addImportPath() or the +\c QML_IMPORT_PATH environment variable. When running the \l {QML Viewer}, you +can also use the \c -I option to add an import path. + + +\section2 Creating installed modules + +As an example, suppose the \c MyQMLProject directory in the \l{Located Modules}{previous example} +was located on the local filesystem at \c C:\qml\projects\MyQMLProject. The \c MyComponents +subdirectory could be made available as an installed module by adding a +\l{Writing a qmldir file}{qmldir file} to the \c MyComponents directory that looked like this: + +\code +Slider 1.0 Slider.qml +CheckBox 1.0 CheckBox.qml +Window 1.0 Window.qml +\endcode + +Providing the path \c C:\qml is added to the QML import path using any of the methods listed previously, +a QML file located anywhere on the local filesystem can then import the module as shown below, +without referring to the module's absolute filesystem location: + +\qml +import projects.MyQMLProject.MyComponents 1.0 + +Window { + Slider { ... } + CheckBox { ... } +} +\endqml + +Installed modules are also accessible as a network resource. If the \c C:\qml directory was hosted +as \c http://www.some-server.com/qml and this URL was added to the QML import path, the above +QML code would work just the same. -When running the \l {QML Viewer}, use the \c -I option to add paths to the import path. +Note that modules imported as a network resource allow only access to components +defined in QML files; components defined by C++ \l{QDeclarativeExtensionPlugin}{QML extension plugins} +are not available. \section2 Creating installed modules in C++ -C++ applications can dynamically define installed modules using -qmlRegisterType(). +C++ applications can define installed modules directly within the application using qmlRegisterType(). +For example, the \l {Tutorial: Writing QML extensions with C++}{Writing QML extensions with C++ tutorial} +defines a C++ class named \c PieChart and makes this type available to QML by calling qmlRegisterType(): + +\qml +qmlRegisterType<PieChart>("Charts", 1, 0, "PieChart"); +\endqml -For \l{QDeclarativeExtensionPlugin}{QML C++ plugins}, the -module URI is automatically passed to QDeclarativeExtensionPlugin::registerTypes(). -The QDeclarativeExtensionPlugin documentation shows how to use this URI -to call qmlRegisterType() to enable the plugin library to be built as -an installed module. Once the plugin is built and installed, the module is importable -in QML, like this: +This allows the application's QML files to use the \c PieChart type by importing the declared +\c Charts module: + +\qml +import Charts 1.0 +\endqml + +For \l{QDeclarativeExtensionPlugin}{QML plugins}, the +module URI is automatically passed to QDeclarativeExtensionPlugin::registerTypes(). This method +can be reimplemented by the developer to register the necessary types for the module. Below is the +\c registerTypes() implementation from the \l{declarative/cppextensions/plugins}{QML plugins} +example: + +\snippet examples/declarative/cppextensions/plugins/plugin.cpp plugin + +Once the plugin is built and installed, and includes a \l{Writing a qmldir file}{qmldir file}, +the module can be imported from QML, like this: \code import com.nokia.TimeExample 1.0 \endcode -A \l{QDeclarativeExtensionPlugin}{QML C++ plugin} also requires a -\l{Writing a qmldir file}{qmldir file} to make it available to the -QML engine. +Unlike QML types defined by QML files, a QML type defined in a C++ extension plugin cannot be loaded by +a module that is imported as a network resource. @@ -233,7 +313,7 @@ Unlike ordinary modules, multiple scripts cannot be imported into the same names A \c qmldir file is a metadata file for a module that maps all type names in the module to versioned QML files. It is required for installed modules, and -location modules that are loaded from a network source. +located modules that are loaded from a network source. It is defined by a plain text file named "qmldir" that contains one or more lines of the form: @@ -283,37 +363,6 @@ containing the plugin file. By default the engine searches for the plugin librar file. The plugin search path can be queried with QDeclarativeEngine::pluginPathList() and modified using QDeclarativeEngine::addPluginPath(). When running the \l {QML Viewer}, use the \c -P option to add paths to the plugin search path. -\target qmldirexample -\section2 Example - -If the components in the \c MyComponents directory from the -\l{Location Modules}{earlier example} were to be made available as a network resource, -the directory would need to contain a \c qmldir file similar to this: - -\code -ComponentA 1.0 ComponentA.qml -ComponentB 1.0 ComponentB.qml -\endcode - -The \c MyComponents directory could then be imported as a module using: - -\code -import "http://the-server-name.com/MyComponents" - -Slider { ... } -CheckBox { ... } -\endcode - -with an optional "1.0" version specification. Notice the import fails if -a later version is used, as the \c qmldir file specifies that these elements -are only available in the 1.0 version. - - -For examples of \c qmldir files for plugins, see the -\l {declarative/cppextensions/plugins}{Plugins} example and -\l {Tutorial: Writing QML extensions with C++}. - - \section1 Debugging The \c QML_IMPORT_TRACE environment variable can be useful for debugging diff --git a/src/declarative/qml/qdeclarativecompiledbindings.cpp b/src/declarative/qml/qdeclarativecompiledbindings.cpp index 3721fb0..a6fcce4 100644 --- a/src/declarative/qml/qdeclarativecompiledbindings.cpp +++ b/src/declarative/qml/qdeclarativecompiledbindings.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ // #define COMPILEDBINDINGS_DEBUG +// #define REGISTER_CLEANUP_DEBUG #include "private/qdeclarativecompiledbindings_p.h" @@ -171,6 +172,24 @@ struct Register { int type; // Optional type void *data[2]; // Object stored here + +#ifdef REGISTER_CLEANUP_DEBUG + Register() { + type = 0; + } + + ~Register() { + int allowedTypes[] = { QMetaType::QObjectStar, QMetaType::QReal, QMetaType::Int, QMetaType::Bool, 0 }; + bool found = (type == 0); + int *ctype = allowedTypes; + while (!found && *ctype) { + found = (*ctype == type); + ++ctype; + } + if (!found) + qWarning("Register leaked of type %d", type); + } +#endif }; } @@ -1412,10 +1431,16 @@ void QDeclarativeCompiledBindingsPrivate::run(int instrIndex, QML_BEGIN_INSTR(CleanupString) registers[instr->cleanup.reg].getstringptr()->~QString(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif QML_END_INSTR(CleanupString) QML_BEGIN_INSTR(CleanupUrl) registers[instr->cleanup.reg].geturlptr()->~QUrl(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif QML_END_INSTR(CleanupUrl) QML_BEGIN_INSTR(Fetch) @@ -1533,10 +1558,19 @@ void QDeclarativeCompiledBindingsPrivate::run(int instrIndex, int type = registers[instr->cleanup.reg].gettype(); if (type == qMetaTypeId<QVariant>()) { registers[instr->cleanup.reg].getvariantptr()->~QVariant(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif } else if (type == QMetaType::QString) { registers[instr->cleanup.reg].getstringptr()->~QString(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif } else if (type == QMetaType::QUrl) { registers[instr->cleanup.reg].geturlptr()->~QUrl(); +#ifdef REGISTER_CLEANUP_DEBUG + registers[instr->cleanup.reg].setUndefined(); +#endif } } QML_END_INSTR(CleanupGeneric) @@ -1749,7 +1783,6 @@ bool QDeclarativeBindingCompilerPrivate::compile(QDeclarativeJS::AST::Node *node done.common.type = Instr::Done; bytecode << done; - return true; } else { // Can we store the final value? if (type.type == QVariant::Int && @@ -1791,12 +1824,12 @@ bool QDeclarativeBindingCompilerPrivate::compile(QDeclarativeJS::AST::Node *node Instr done; done.common.type = Instr::Done; bytecode << done; - - return true; } else { return false; } } + + return true; } bool QDeclarativeBindingCompilerPrivate::parseExpression(QDeclarativeJS::AST::Node *node, Result &type) @@ -2236,6 +2269,8 @@ bool QDeclarativeBindingCompilerPrivate::stringArith(Result &type, const Result if (lhsTmp != -1) releaseReg(lhsTmp); if (rhsTmp != -1) releaseReg(rhsTmp); + releaseReg(lhs.reg); + releaseReg(rhs.reg); return true; } diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index a0633a6..854d910 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -2113,10 +2113,11 @@ QVariant QDeclarativeEnginePrivate::scriptValueToVariant(const QScriptValue &val /*! Adds \a path as a directory where the engine searches for installed modules in a URL-based directory structure. + The \a path may be a local filesystem directory or a URL. The newly added \a path will be first in the importPathList(). - \sa setImportPathList() + \sa setImportPathList(), \l {QML Modules} */ void QDeclarativeEngine::addImportPath(const QString& path) { diff --git a/src/declarative/qml/qdeclarativeimport.cpp b/src/declarative/qml/qdeclarativeimport.cpp index 22dcb44..87183c4 100644 --- a/src/declarative/qml/qdeclarativeimport.cpp +++ b/src/declarative/qml/qdeclarativeimport.cpp @@ -712,6 +712,7 @@ bool QDeclarativeImportedNamespace::find(const QByteArray& type, int *vmajor, in /*! \class QDeclarativeImportDatabase \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine. +\internal */ QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e) : engine(e) @@ -926,16 +927,25 @@ QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const #endif } +/*! + \internal +*/ QStringList QDeclarativeImportDatabase::pluginPathList() const { return filePluginPath; } +/*! + \internal +*/ void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths) { filePluginPath = paths; } +/*! + \internal +*/ void QDeclarativeImportDatabase::addPluginPath(const QString& path) { if (qmlImportTrace()) @@ -950,6 +960,9 @@ void QDeclarativeImportDatabase::addPluginPath(const QString& path) } } +/*! + \internal +*/ void QDeclarativeImportDatabase::addImportPath(const QString& path) { if (qmlImportTrace()) @@ -974,17 +987,25 @@ void QDeclarativeImportDatabase::addImportPath(const QString& path) fileImportPath.prepend(cPath); } +/*! + \internal +*/ QStringList QDeclarativeImportDatabase::importPathList() const { return fileImportPath; } +/*! + \internal +*/ void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths) { fileImportPath = paths; } - +/*! + \internal +*/ bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString) { if (qmlImportTrace()) diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/ComponentA.qml b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/ComponentA.qml new file mode 100644 index 0000000..49613aa --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/ComponentA.qml @@ -0,0 +1,3 @@ +import QtQuick 1.0 + +Item {} diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/ComponentB.qml b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/ComponentB.qml new file mode 100644 index 0000000..f19a336 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/ComponentB.qml @@ -0,0 +1,4 @@ +import QtQuick 1.0 + +Item {} + diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/qmldir b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/qmldir new file mode 100644 index 0000000..167bb10 --- /dev/null +++ b/tests/auto/declarative/qdeclarativemoduleplugin/imports/com/nokia/PureQmlModule/qmldir @@ -0,0 +1,3 @@ +ComponentA 1.0 ComponentA.qml +ComponentB 1.0 ComponentB.qml + diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp index a4e9270..9ec0f84 100644 --- a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp +++ b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.cpp @@ -44,6 +44,13 @@ #include <QtDeclarative/qdeclarativecomponent.h> #include <QDebug> +#include "../shared/testhttpserver.h" +#include "../../../shared/util.h" + +#define SERVER_ADDR "http://127.0.0.1:14450" +#define SERVER_PORT 14450 + + class tst_qdeclarativemoduleplugin : public QObject { Q_OBJECT @@ -58,6 +65,8 @@ private slots: void importsPlugin21(); void incorrectPluginCase(); void importPluginWithQmlFile(); + void remoteImportWithQuotedUrl(); + void remoteImportWithUnquotedUri(); }; #ifdef Q_OS_SYMBIAN @@ -193,6 +202,50 @@ void tst_qdeclarativemoduleplugin::importPluginWithQmlFile() delete object; } +void tst_qdeclarativemoduleplugin::remoteImportWithQuotedUrl() +{ + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/imports"); + + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine); + component.setData("import \"http://127.0.0.1:14450/com/nokia/PureQmlModule\" \nComponentA { width: 300; ComponentB{} }", QUrl()); + + QTRY_COMPARE(component.status(), QDeclarativeComponent::Ready); + QObject *object = component.create(); + QCOMPARE(object->property("width").toInt(), 300); + QVERIFY(object != 0); + delete object; + + foreach (QDeclarativeError err, component.errors()) + qWarning() << err; + VERIFY_ERRORS(0); +} + +void tst_qdeclarativemoduleplugin::remoteImportWithUnquotedUri() +{ + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/imports"); + + QDeclarativeEngine engine; + engine.addImportPath(QLatin1String(SRCDIR) + QDir::separator() + QLatin1String("imports")); + QDeclarativeComponent component(&engine); + component.setData("import com.nokia.PureQmlModule 1.0 \nComponentA { width: 300; ComponentB{} }", QUrl()); + + + QTRY_COMPARE(component.status(), QDeclarativeComponent::Ready); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("width").toInt(), 300); + delete object; + + foreach (QDeclarativeError err, component.errors()) + qWarning() << err; + VERIFY_ERRORS(0); +} + QTEST_MAIN(tst_qdeclarativemoduleplugin) #include "tst_qdeclarativemoduleplugin.moc" diff --git a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.pro b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.pro index fb3630f..a92d3a2 100644 --- a/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.pro +++ b/tests/auto/declarative/qdeclarativemoduleplugin/tst_qdeclarativemoduleplugin.pro @@ -1,6 +1,9 @@ load(qttest_p4) -SOURCES = tst_qdeclarativemoduleplugin.cpp -QT += declarative + +HEADERS = ../shared/testhttpserver.h +SOURCES = tst_qdeclarativemoduleplugin.cpp \ + ../shared/testhttpserver.cpp +QT += declarative network CONFIG -= app_bundle symbian: { |