1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativeimport_p.h"
43
44 #include <QtCore/qdebug.h>
45 #include <QtCore/qdir.h>
46 #include <QtCore/qfileinfo.h>
47 #include <QtCore/qpluginloader.h>
48 #include <QtCore/qlibraryinfo.h>
49 #include <QtCore/qalgorithms.h>
50 #include <QtDeclarative/qdeclarativeextensioninterface.h>
51 #include <private/qdeclarativeglobal_p.h>
52 #include <private/qdeclarativetypenamecache_p.h>
53 #include <private/qdeclarativeengine_p.h>
54
55 #ifdef Q_OS_SYMBIAN
56 #include "private/qcore_symbian_p.h"
57 #endif
58
59 QT_BEGIN_NAMESPACE
60
DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace,QML_IMPORT_TRACE)61 DEFINE_BOOL_CONFIG_OPTION(qmlImportTrace, QML_IMPORT_TRACE)
62 DEFINE_BOOL_CONFIG_OPTION(qmlCheckTypes, QML_CHECK_TYPES)
63
64 static bool greaterThan(const QString &s1, const QString &s2)
65 {
66 return s1 > s2;
67 }
68
69 typedef QMap<QString, QString> StringStringMap;
70 Q_GLOBAL_STATIC(StringStringMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri
71
72 class QDeclarativeImportedNamespace
73 {
74 public:
75 QStringList uris;
76 QStringList urls;
77 QList<int> majversions;
78 QList<int> minversions;
79 QList<bool> isLibrary;
80 QList<QDeclarativeDirComponents> qmlDirComponents;
81
82
83 bool find_helper(QDeclarativeTypeLoader *typeLoader, int i, const QByteArray& type, int *vmajor, int *vminor,
84 QDeclarativeType** type_return, QUrl* url_return,
85 QUrl *base = 0, bool *typeRecursionDetected = 0);
86 bool find(QDeclarativeTypeLoader *typeLoader, const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
87 QUrl* url_return, QUrl *base = 0, QString *errorString = 0);
88 };
89
90 class QDeclarativeImportsPrivate {
91 public:
92 QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader);
93 ~QDeclarativeImportsPrivate();
94
95 bool importExtension(const QString &absoluteFilePath, const QString &uri,
96 QDeclarativeImportDatabase *database, QDeclarativeDirComponents* components,
97 QString *errorString);
98
99 QString resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database);
100 bool add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
101 const QString& uri_arg, const QString& prefix,
102 int vmaj, int vmin, QDeclarativeScriptParser::Import::Type importType,
103 QDeclarativeImportDatabase *database, QString *errorString);
104 bool find(const QByteArray& type, int *vmajor, int *vminor,
105 QDeclarativeType** type_return, QUrl* url_return, QString *errorString);
106
107 QDeclarativeImportedNamespace *findNamespace(const QString& type);
108
109 QUrl base;
110 int ref;
111
112 QSet<QString> qmlDirFilesForWhichPluginsHaveBeenLoaded;
113 QDeclarativeImportedNamespace unqualifiedset;
114 QHash<QString,QDeclarativeImportedNamespace* > set;
115 QDeclarativeTypeLoader *typeLoader;
116 };
117
118 /*!
119 \class QDeclarativeImports
120 \brief The QDeclarativeImports class encapsulates one QML document's import statements.
121 \internal
122 */
QDeclarativeImports(const QDeclarativeImports & copy)123 QDeclarativeImports::QDeclarativeImports(const QDeclarativeImports ©)
124 : d(copy.d)
125 {
126 ++d->ref;
127 }
128
129 QDeclarativeImports &
operator =(const QDeclarativeImports & copy)130 QDeclarativeImports::operator =(const QDeclarativeImports ©)
131 {
132 ++copy.d->ref;
133 if (--d->ref == 0)
134 delete d;
135 d = copy.d;
136 return *this;
137 }
138
QDeclarativeImports()139 QDeclarativeImports::QDeclarativeImports()
140 : d(new QDeclarativeImportsPrivate(0)){
141 }
142
QDeclarativeImports(QDeclarativeTypeLoader * typeLoader)143 QDeclarativeImports::QDeclarativeImports(QDeclarativeTypeLoader *typeLoader)
144 : d(new QDeclarativeImportsPrivate(typeLoader)){
145 }
146
~QDeclarativeImports()147 QDeclarativeImports::~QDeclarativeImports()
148 {
149 if (--d->ref == 0)
150 delete d;
151 }
152
153 /*!
154 Sets the base URL to be used for all relative file imports added.
155 */
setBaseUrl(const QUrl & url)156 void QDeclarativeImports::setBaseUrl(const QUrl& url)
157 {
158 d->base = url;
159 }
160
161 /*!
162 Returns the base URL to be used for all relative file imports added.
163 */
baseUrl() const164 QUrl QDeclarativeImports::baseUrl() const
165 {
166 return d->base;
167 }
168
169 static QDeclarativeTypeNameCache *
cacheForNamespace(QDeclarativeEngine * engine,const QDeclarativeImportedNamespace & set,QDeclarativeTypeNameCache * cache)170 cacheForNamespace(QDeclarativeEngine *engine, const QDeclarativeImportedNamespace &set,
171 QDeclarativeTypeNameCache *cache)
172 {
173 if (!cache)
174 cache = new QDeclarativeTypeNameCache(engine);
175
176 QList<QDeclarativeType *> types = QDeclarativeMetaType::qmlTypes();
177
178 for (int ii = 0; ii < set.uris.count(); ++ii) {
179 QByteArray base = set.uris.at(ii).toUtf8() + '/';
180 int major = set.majversions.at(ii);
181 int minor = set.minversions.at(ii);
182
183 foreach (QDeclarativeType *type, types) {
184 if (type->qmlTypeName().startsWith(base) &&
185 type->qmlTypeName().lastIndexOf('/') == (base.length() - 1) &&
186 type->availableInVersion(major,minor))
187 {
188 QString name = QString::fromUtf8(type->qmlTypeName().mid(base.length()));
189
190 cache->add(name, type);
191 }
192 }
193 }
194
195 return cache;
196 }
197
populateCache(QDeclarativeTypeNameCache * cache,QDeclarativeEngine * engine) const198 void QDeclarativeImports::populateCache(QDeclarativeTypeNameCache *cache, QDeclarativeEngine *engine) const
199 {
200 const QDeclarativeImportedNamespace &set = d->unqualifiedset;
201
202 for (QHash<QString,QDeclarativeImportedNamespace* >::ConstIterator iter = d->set.begin();
203 iter != d->set.end(); ++iter) {
204
205 QDeclarativeTypeNameCache::Data *d = cache->data(iter.key());
206 if (d) {
207 if (!d->typeNamespace)
208 cacheForNamespace(engine, *(*iter), d->typeNamespace);
209 } else {
210 QDeclarativeTypeNameCache *nc = cacheForNamespace(engine, *(*iter), 0);
211 cache->add(iter.key(), nc);
212 nc->release();
213 }
214 }
215
216 cacheForNamespace(engine, set, cache);
217 }
218
219 /*!
220 \internal
221
222 The given (namespace qualified) \a type is resolved to either
223 \list
224 \o a QDeclarativeImportedNamespace stored at \a ns_return,
225 \o a QDeclarativeType stored at \a type_return, or
226 \o a component located at \a url_return.
227 \endlist
228
229 If any return pointer is 0, the corresponding search is not done.
230
231 \sa addImport()
232 */
resolveType(const QByteArray & type,QDeclarativeType ** type_return,QUrl * url_return,int * vmaj,int * vmin,QDeclarativeImportedNamespace ** ns_return,QString * errorString) const233 bool QDeclarativeImports::resolveType(const QByteArray& type,
234 QDeclarativeType** type_return, QUrl* url_return, int *vmaj, int *vmin,
235 QDeclarativeImportedNamespace** ns_return, QString *errorString) const
236 {
237 QDeclarativeImportedNamespace* ns = d->findNamespace(QString::fromUtf8(type));
238 if (ns) {
239 if (ns_return)
240 *ns_return = ns;
241 return true;
242 }
243 if (type_return || url_return) {
244 if (d->find(type,vmaj,vmin,type_return,url_return, errorString)) {
245 if (qmlImportTrace()) {
246 if (type_return && *type_return && url_return && !url_return->isEmpty())
247 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
248 << type << " => " << (*type_return)->typeName() << " " << *url_return;
249 if (type_return && *type_return)
250 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
251 << type << " => " << (*type_return)->typeName();
252 if (url_return && !url_return->isEmpty())
253 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::resolveType: "
254 << type << " => " << *url_return;
255 }
256 return true;
257 }
258 }
259 return false;
260 }
261
262 /*!
263 \internal
264
265 Searching \e only in the namespace \a ns (previously returned in a call to
266 resolveType(), \a type is found and returned to either
267 a QDeclarativeType stored at \a type_return, or
268 a component located at \a url_return.
269
270 If either return pointer is 0, the corresponding search is not done.
271 */
resolveType(QDeclarativeImportedNamespace * ns,const QByteArray & type,QDeclarativeType ** type_return,QUrl * url_return,int * vmaj,int * vmin) const272 bool QDeclarativeImports::resolveType(QDeclarativeImportedNamespace* ns, const QByteArray& type,
273 QDeclarativeType** type_return, QUrl* url_return,
274 int *vmaj, int *vmin) const
275 {
276 Q_ASSERT(d->typeLoader);
277 return ns->find(d->typeLoader,type,vmaj,vmin,type_return,url_return);
278 }
279
find_helper(QDeclarativeTypeLoader * typeLoader,int i,const QByteArray & type,int * vmajor,int * vminor,QDeclarativeType ** type_return,QUrl * url_return,QUrl * base,bool * typeRecursionDetected)280 bool QDeclarativeImportedNamespace::find_helper(QDeclarativeTypeLoader *typeLoader, int i, const QByteArray& type, int *vmajor, int *vminor,
281 QDeclarativeType** type_return, QUrl* url_return,
282 QUrl *base, bool *typeRecursionDetected)
283 {
284 int vmaj = majversions.at(i);
285 int vmin = minversions.at(i);
286
287 QByteArray qt = uris.at(i).toUtf8();
288 qt += '/';
289 qt += type;
290
291 QDeclarativeType *t = QDeclarativeMetaType::qmlType(qt,vmaj,vmin);
292 if (t) {
293 if (vmajor) *vmajor = vmaj;
294 if (vminor) *vminor = vmin;
295 if (type_return)
296 *type_return = t;
297 return true;
298 }
299
300 QDeclarativeDirComponents qmldircomponents = qmlDirComponents.at(i);
301
302 bool typeWasDeclaredInQmldir = false;
303 if (!qmldircomponents.isEmpty()) {
304 const QString typeName = QString::fromUtf8(type);
305 foreach (const QDeclarativeDirParser::Component &c, qmldircomponents) {
306 if (c.typeName == typeName) {
307 typeWasDeclaredInQmldir = true;
308
309 // importing version -1 means import ALL versions
310 if ((vmaj == -1) || (c.majorVersion < vmaj || (c.majorVersion == vmaj && vmin >= c.minorVersion))) {
311 QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml"));
312 QUrl candidate = url.resolved(QUrl(c.fileName));
313 if (c.internal && base) {
314 if (base->resolved(QUrl(c.fileName)) != candidate)
315 continue; // failed attempt to access an internal type
316 }
317 if (base && *base == candidate) {
318 if (typeRecursionDetected)
319 *typeRecursionDetected = true;
320 continue; // no recursion
321 }
322 if (url_return)
323 *url_return = candidate;
324 return true;
325 }
326 }
327 }
328 }
329
330 if (!typeWasDeclaredInQmldir && !isLibrary.at(i)) {
331 // XXX search non-files too! (eg. zip files, see QT-524)
332 QUrl url = QUrl(urls.at(i) + QLatin1Char('/') + QString::fromUtf8(type) + QLatin1String(".qml"));
333 QString file = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url);
334 if (!typeLoader->absoluteFilePath(file).isEmpty()) {
335 if (base && *base == url) { // no recursion
336 if (typeRecursionDetected)
337 *typeRecursionDetected = true;
338 } else {
339 if (url_return)
340 *url_return = url;
341 return true;
342 }
343 }
344 }
345 return false;
346 }
347
QDeclarativeImportsPrivate(QDeclarativeTypeLoader * loader)348 QDeclarativeImportsPrivate::QDeclarativeImportsPrivate(QDeclarativeTypeLoader *loader)
349 : ref(1), typeLoader(loader){
350 }
351
~QDeclarativeImportsPrivate()352 QDeclarativeImportsPrivate::~QDeclarativeImportsPrivate()
353 {
354 foreach (QDeclarativeImportedNamespace* s, set.values())
355 delete s;
356 }
357
importExtension(const QString & absoluteFilePath,const QString & uri,QDeclarativeImportDatabase * database,QDeclarativeDirComponents * components,QString * errorString)358 bool QDeclarativeImportsPrivate::importExtension(const QString &absoluteFilePath, const QString &uri,
359 QDeclarativeImportDatabase *database,
360 QDeclarativeDirComponents* components, QString *errorString)
361 {
362 Q_ASSERT(typeLoader);
363 const QDeclarativeDirParser *qmldirParser = typeLoader->qmlDirParser(absoluteFilePath);
364 if (qmldirParser->hasError()) {
365 if (errorString) {
366 const QList<QDeclarativeError> qmldirErrors = qmldirParser->errors(uri);
367 for (int i = 0; i < qmldirErrors.size(); ++i)
368 *errorString += qmldirErrors.at(i).description();
369 }
370 return false;
371 }
372
373 if (! qmlDirFilesForWhichPluginsHaveBeenLoaded.contains(absoluteFilePath)) {
374 qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(absoluteFilePath);
375
376 QDir dir = QFileInfo(absoluteFilePath).dir();
377 foreach (const QDeclarativeDirParser::Plugin &plugin, qmldirParser->plugins()) {
378
379 QString resolvedFilePath = database->resolvePlugin(dir, plugin.path, plugin.name);
380 #if defined(QT_LIBINFIX) && defined(Q_OS_SYMBIAN)
381 if (resolvedFilePath.isEmpty()) {
382 // In case of libinfixed build, attempt to load libinfixed version, too.
383 QString infixedPluginName = plugin.name + QLatin1String(QT_LIBINFIX);
384 resolvedFilePath = database->resolvePlugin(dir, plugin.path, infixedPluginName);
385 }
386 #endif
387 if (!resolvedFilePath.isEmpty()) {
388 if (!database->importPlugin(resolvedFilePath, uri, errorString)) {
389 if (errorString)
390 *errorString = QDeclarativeImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri).arg(*errorString);
391 return false;
392 }
393 } else {
394 if (errorString)
395 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(plugin.name);
396 return false;
397 }
398 }
399 }
400
401 if (components)
402 *components = qmldirParser->components();
403
404 return true;
405 }
406
resolvedUri(const QString & dir_arg,QDeclarativeImportDatabase * database)407 QString QDeclarativeImportsPrivate::resolvedUri(const QString &dir_arg, QDeclarativeImportDatabase *database)
408 {
409 QString dir = dir_arg;
410 if (dir.endsWith(QLatin1Char('/')) || dir.endsWith(QLatin1Char('\\')))
411 dir.chop(1);
412
413 QStringList paths = database->fileImportPath;
414 qSort(paths.begin(), paths.end(), greaterThan); // Ensure subdirs preceed their parents.
415
416 QString stableRelativePath = dir;
417 foreach(const QString &path, paths) {
418 if (dir.startsWith(path)) {
419 stableRelativePath = dir.mid(path.length()+1);
420 break;
421 }
422 }
423
424 stableRelativePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
425
426 // remove optional versioning in dot notation from uri
427 int lastSlash = stableRelativePath.lastIndexOf(QLatin1Char('/'));
428 if (lastSlash >= 0) {
429 int versionDot = stableRelativePath.indexOf(QLatin1Char('.'), lastSlash);
430 if (versionDot >= 0)
431 stableRelativePath = stableRelativePath.left(versionDot);
432 }
433
434 stableRelativePath.replace(QLatin1Char('/'), QLatin1Char('.'));
435 return stableRelativePath;
436 }
437
add(const QDeclarativeDirComponents & qmldircomponentsnetwork,const QString & uri_arg,const QString & prefix,int vmaj,int vmin,QDeclarativeScriptParser::Import::Type importType,QDeclarativeImportDatabase * database,QString * errorString)438 bool QDeclarativeImportsPrivate::add(const QDeclarativeDirComponents &qmldircomponentsnetwork,
439 const QString& uri_arg, const QString& prefix, int vmaj, int vmin,
440 QDeclarativeScriptParser::Import::Type importType,
441 QDeclarativeImportDatabase *database, QString *errorString)
442 {
443 Q_ASSERT(typeLoader);
444 QDeclarativeDirComponents qmldircomponents = qmldircomponentsnetwork;
445 QString uri = uri_arg;
446 QDeclarativeImportedNamespace *s;
447 if (prefix.isEmpty()) {
448 s = &unqualifiedset;
449 } else {
450 s = set.value(prefix);
451 if (!s)
452 set.insert(prefix,(s=new QDeclarativeImportedNamespace));
453 }
454
455 bool appendInstead = false;
456 if (importType == QDeclarativeScriptParser::Import::Implicit) {
457 //Treat same as a File import, but lower precedence
458 appendInstead = true;
459 importType = QDeclarativeScriptParser::Import::File;
460 }
461
462 QString url = uri;
463 bool versionFound = false;
464 if (importType == QDeclarativeScriptParser::Import::Library) {
465 url.replace(QLatin1Char('.'), QLatin1Char('/'));
466 bool found = false;
467 QString dir;
468 QString qmldir;
469
470 // step 1: search for extension with fully encoded version number
471 if (vmaj >= 0 && vmin >= 0) {
472 foreach (const QString &p, database->fileImportPath) {
473 dir = p+QLatin1Char('/')+url;
474 qmldir = dir+QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin)+QLatin1String("/qmldir");
475
476 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
477 if (!absoluteFilePath.isEmpty()) {
478 found = true;
479
480 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(QLatin1Char('/')));
481 url = QUrl::fromLocalFile(absolutePath).toString();
482 uri = resolvedUri(dir, database);
483 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
484 return false;
485 break;
486 }
487 }
488 }
489 // step 2: search for extension with encoded version major
490 if (vmaj >= 0 && vmin >= 0) {
491 foreach (const QString &p, database->fileImportPath) {
492 dir = p+QLatin1Char('/')+url;
493 qmldir = dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir");
494
495 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
496 if (!absoluteFilePath.isEmpty()) {
497 found = true;
498
499 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(QLatin1Char('/')));
500 url = QUrl::fromLocalFile(absolutePath).toString();
501 uri = resolvedUri(dir, database);
502 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
503 return false;
504 break;
505 }
506 }
507 }
508 if (!found) {
509 // step 3: search for extension without version number
510
511 foreach (const QString &p, database->fileImportPath) {
512 dir = p+QLatin1Char('/')+url;
513 qmldir = dir+QLatin1String("/qmldir");
514
515 QString absoluteFilePath = typeLoader->absoluteFilePath(qmldir);
516 if (!absoluteFilePath.isEmpty()) {
517 found = true;
518
519 QString absolutePath = absoluteFilePath.left(absoluteFilePath.lastIndexOf(QLatin1Char('/')));
520 url = QUrl::fromLocalFile(absolutePath).toString();
521 uri = resolvedUri(dir, database);
522 if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, errorString))
523 return false;
524 break;
525 }
526 }
527 }
528
529 if (QDeclarativeMetaType::isModule(uri.toUtf8(), vmaj, vmin))
530 versionFound = true;
531
532 //Load any type->file mappings registered for this uri
533 qmldircomponents << QDeclarativeMetaType::qmlComponents(uri.toUtf8(), vmaj, vmin);
534
535 if (!versionFound && qmldircomponents.isEmpty()) {
536 if (errorString) {
537 bool anyversion = QDeclarativeMetaType::isModule(uri.toUtf8(), -1, -1);
538 if (anyversion)
539 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin);
540 else
541 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" is not installed").arg(uri_arg);
542 }
543 return false;
544 }
545 } else {
546
547 if (importType == QDeclarativeScriptParser::Import::File && qmldircomponents.isEmpty()) {
548 QUrl importUrl = base.resolved(QUrl(uri + QLatin1String("/qmldir")));
549 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl);
550 if (!localFileOrQrc.isEmpty()) {
551 QString dir = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
552 QFileInfo dirinfo(dir);
553 if (dir.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
554 if (errorString)
555 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri_arg);
556 return false; // local import dirs must exist
557 }
558 uri = resolvedUri(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri))), database);
559 if (uri.endsWith(QLatin1Char('/')))
560 uri.chop(1);
561 if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
562 if (!importExtension(localFileOrQrc,uri,database,&qmldircomponents,errorString))
563 return false;
564 }
565 } else {
566 if (prefix.isEmpty()) {
567 // directory must at least exist for valid import
568 QString localFileOrQrc = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(base.resolved(QUrl(uri)));
569 QFileInfo dirinfo(localFileOrQrc);
570 if (localFileOrQrc.isEmpty() || !dirinfo.exists() || !dirinfo.isDir()) {
571 if (errorString) {
572 if (localFileOrQrc.isEmpty())
573 *errorString = QDeclarativeImportDatabase::tr("import \"%1\" has no qmldir and no namespace").arg(uri);
574 else
575 *errorString = QDeclarativeImportDatabase::tr("\"%1\": no such directory").arg(uri);
576 }
577 return false;
578 }
579 }
580 }
581 }
582
583 url = base.resolved(QUrl(url)).toString();
584 if (url.endsWith(QLatin1Char('/')))
585 url.chop(1);
586 }
587
588 if (!versionFound && vmaj > -1 && vmin > -1 && !qmldircomponents.isEmpty()) {
589 QList<QDeclarativeDirParser::Component>::ConstIterator it = qmldircomponents.begin();
590 int lowest_maj = INT_MAX;
591 int lowest_min = INT_MAX;
592 int highest_maj = INT_MIN;
593 int highest_min = INT_MIN;
594 for (; it != qmldircomponents.end(); ++it) {
595 if (it->majorVersion > highest_maj || (it->majorVersion == highest_maj && it->minorVersion > highest_min)) {
596 highest_maj = it->majorVersion;
597 highest_min = it->minorVersion;
598 }
599 if (it->majorVersion < lowest_maj || (it->majorVersion == lowest_maj && it->minorVersion < lowest_min)) {
600 lowest_maj = it->majorVersion;
601 lowest_min = it->minorVersion;
602 }
603 }
604 if (lowest_maj > vmaj || (lowest_maj == vmaj && lowest_min > vmin)
605 || highest_maj < vmaj || (highest_maj == vmaj && highest_min < vmin))
606 {
607 *errorString = QDeclarativeImportDatabase::tr("module \"%1\" version %2.%3 is not installed").arg(uri_arg).arg(vmaj).arg(vmin);
608 return false;
609 }
610 }
611
612 if (appendInstead) {
613 s->uris.append(uri);
614 s->urls.append(url);
615 s->majversions.append(vmaj);
616 s->minversions.append(vmin);
617 s->isLibrary.append(importType == QDeclarativeScriptParser::Import::Library);
618 s->qmlDirComponents.append(qmldircomponents);
619 } else {
620 s->uris.prepend(uri);
621 s->urls.prepend(url);
622 s->majversions.prepend(vmaj);
623 s->minversions.prepend(vmin);
624 s->isLibrary.prepend(importType == QDeclarativeScriptParser::Import::Library);
625 s->qmlDirComponents.prepend(qmldircomponents);
626 }
627 return true;
628 }
629
find(const QByteArray & type,int * vmajor,int * vminor,QDeclarativeType ** type_return,QUrl * url_return,QString * errorString)630 bool QDeclarativeImportsPrivate::find(const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
631 QUrl* url_return, QString *errorString)
632 {
633 Q_ASSERT(typeLoader);
634 QDeclarativeImportedNamespace *s = 0;
635 int slash = type.indexOf('/');
636 if (slash >= 0) {
637 QString namespaceName = QString::fromUtf8(type.left(slash));
638 s = set.value(namespaceName);
639 if (!s) {
640 if (errorString)
641 *errorString = QDeclarativeImportDatabase::tr("- %1 is not a namespace").arg(namespaceName);
642 return false;
643 }
644 int nslash = type.indexOf('/',slash+1);
645 if (nslash > 0) {
646 if (errorString)
647 *errorString = QDeclarativeImportDatabase::tr("- nested namespaces not allowed");
648 return false;
649 }
650 } else {
651 s = &unqualifiedset;
652 }
653 QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower)
654 if (s) {
655 if (s->find(typeLoader, unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errorString))
656 return true;
657 if (s->urls.count() == 1 && !s->isLibrary[0] && url_return && s != &unqualifiedset) {
658 // qualified, and only 1 url
659 *url_return = QUrl(s->urls[0]+QLatin1Char('/')).resolved(QUrl(QString::fromUtf8(unqualifiedtype) + QLatin1String(".qml")));
660 return true;
661 }
662 }
663
664 return false;
665 }
666
findNamespace(const QString & type)667 QDeclarativeImportedNamespace *QDeclarativeImportsPrivate::findNamespace(const QString& type)
668 {
669 return set.value(type);
670 }
671
find(QDeclarativeTypeLoader * typeLoader,const QByteArray & type,int * vmajor,int * vminor,QDeclarativeType ** type_return,QUrl * url_return,QUrl * base,QString * errorString)672 bool QDeclarativeImportedNamespace::find(QDeclarativeTypeLoader *typeLoader, const QByteArray& type, int *vmajor, int *vminor, QDeclarativeType** type_return,
673 QUrl* url_return, QUrl *base, QString *errorString)
674 {
675 bool typeRecursionDetected = false;
676 for (int i=0; i<urls.count(); ++i) {
677 if (find_helper(typeLoader, i, type, vmajor, vminor, type_return, url_return, base, &typeRecursionDetected)) {
678 if (qmlCheckTypes()) {
679 // check for type clashes
680 for (int j = i+1; j<urls.count(); ++j) {
681 if (find_helper(typeLoader, j, type, vmajor, vminor, 0, 0, base)) {
682 if (errorString) {
683 QString u1 = urls.at(i);
684 QString u2 = urls.at(j);
685 if (base) {
686 QString b = base->toString();
687 int slash = b.lastIndexOf(QLatin1Char('/'));
688 if (slash >= 0) {
689 b = b.left(slash+1);
690 QString l = b.left(slash);
691 if (u1.startsWith(b))
692 u1 = u1.mid(b.count());
693 else if (u1 == l)
694 u1 = QDeclarativeImportDatabase::tr("local directory");
695 if (u2.startsWith(b))
696 u2 = u2.mid(b.count());
697 else if (u2 == l)
698 u2 = QDeclarativeImportDatabase::tr("local directory");
699 }
700 }
701
702 if (u1 != u2)
703 *errorString
704 = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 and in %2")
705 .arg(u1).arg(u2);
706 else
707 *errorString
708 = QDeclarativeImportDatabase::tr("is ambiguous. Found in %1 in version %2.%3 and %4.%5")
709 .arg(u1)
710 .arg(majversions.at(i)).arg(minversions.at(i))
711 .arg(majversions.at(j)).arg(minversions.at(j));
712 }
713 return false;
714 }
715 }
716 }
717 return true;
718 }
719 }
720 if (errorString) {
721 if (typeRecursionDetected)
722 *errorString = QDeclarativeImportDatabase::tr("is instantiated recursively");
723 else
724 *errorString = QDeclarativeImportDatabase::tr("is not a type");
725 }
726 return false;
727 }
728
729 /*!
730 \class QDeclarativeImportDatabase
731 \brief The QDeclarativeImportDatabase class manages the QML imports for a QDeclarativeEngine.
732 \internal
733 */
QDeclarativeImportDatabase(QDeclarativeEngine * e)734 QDeclarativeImportDatabase::QDeclarativeImportDatabase(QDeclarativeEngine *e)
735 : engine(e)
736 {
737 filePluginPath << QLatin1String(".");
738
739 // Search order is applicationDirPath(), $QML_IMPORT_PATH, QLibraryInfo::ImportsPath
740
741 #ifndef QT_NO_SETTINGS
742 QString installImportsPath = QLibraryInfo::location(QLibraryInfo::ImportsPath);
743
744 #if defined(Q_OS_SYMBIAN)
745 // Append imports path for all available drives in Symbian
746 if (installImportsPath.at(1) != QChar(QLatin1Char(':'))) {
747 QString tempPath = installImportsPath;
748 if (tempPath.at(tempPath.length() - 1) != QDir::separator()) {
749 tempPath += QDir::separator();
750 }
751 RFs& fs = qt_s60GetRFs();
752 TPtrC tempPathPtr(reinterpret_cast<const TText*> (tempPath.constData()));
753 // Symbian searches should start from Y:. Fix start drive otherwise TFindFile starts from the session drive
754 _LIT(KStartDir, "Y:");
755 TFileName dirPath(KStartDir);
756 dirPath.Append(tempPathPtr);
757 TFindFile finder(fs);
758 TInt err = finder.FindByDir(tempPathPtr, dirPath);
759 while (err == KErrNone) {
760 QString foundDir(reinterpret_cast<const QChar *>(finder.File().Ptr()),
761 finder.File().Length());
762 foundDir = QDir(foundDir).canonicalPath();
763 addImportPath(foundDir);
764 err = finder.Find();
765 }
766 // TFindFile found the directories in the order we want, but addImportPath reverses it.
767 // Reverse the order again to get it right.
768 QAlgorithmsPrivate::qReverse(fileImportPath.begin(), fileImportPath.end());
769 } else {
770 addImportPath(installImportsPath);
771 }
772 #else
773 addImportPath(installImportsPath);
774 #endif
775
776 #endif // QT_NO_SETTINGS
777
778 // env import paths
779 QByteArray envImportPath = qgetenv("QML_IMPORT_PATH");
780 if (!envImportPath.isEmpty()) {
781 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
782 QLatin1Char pathSep(';');
783 #else
784 QLatin1Char pathSep(':');
785 #endif
786 QStringList paths = QString::fromLatin1(envImportPath).split(pathSep, QString::SkipEmptyParts);
787 for (int ii = paths.count() - 1; ii >= 0; --ii)
788 addImportPath(paths.at(ii));
789 }
790
791 addImportPath(QCoreApplication::applicationDirPath());
792 }
793
~QDeclarativeImportDatabase()794 QDeclarativeImportDatabase::~QDeclarativeImportDatabase()
795 {
796 }
797
798 /*!
799 \internal
800
801 Adds information to \a imports such that subsequent calls to resolveType()
802 will resolve types qualified by \a prefix by considering types found at the given \a uri.
803
804 The uri is either a directory (if importType is FileImport), or a URI resolved using paths
805 added via addImportPath() (if importType is LibraryImport).
806
807 The \a prefix may be empty, in which case the import location is considered for
808 unqualified types.
809
810 The base URL must already have been set with Import::setBaseUrl().
811 */
addImport(QDeclarativeImportDatabase * importDb,const QString & uri,const QString & prefix,int vmaj,int vmin,QDeclarativeScriptParser::Import::Type importType,const QDeclarativeDirComponents & qmldircomponentsnetwork,QString * errorString)812 bool QDeclarativeImports::addImport(QDeclarativeImportDatabase *importDb,
813 const QString& uri, const QString& prefix, int vmaj, int vmin,
814 QDeclarativeScriptParser::Import::Type importType,
815 const QDeclarativeDirComponents &qmldircomponentsnetwork,
816 QString *errorString)
817 {
818 if (qmlImportTrace())
819 qDebug().nospace() << "QDeclarativeImports(" << qPrintable(baseUrl().toString()) << ")" << "::addImport: "
820 << uri << " " << vmaj << '.' << vmin << " "
821 << (importType==QDeclarativeScriptParser::Import::Library? "Library" : "File")
822 << " as " << prefix;
823
824 return d->add(qmldircomponentsnetwork, uri, prefix, vmaj, vmin, importType, importDb, errorString);
825 }
826
827 /*!
828 \internal
829
830 Returns the result of the merge of \a baseName with \a path, \a suffixes, and \a prefix.
831 The \a prefix must contain the dot.
832
833 \a qmldirPath is the location of the qmldir file.
834 */
resolvePlugin(const QDir & qmldirPath,const QString & qmldirPluginPath,const QString & baseName,const QStringList & suffixes,const QString & prefix)835 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
836 const QString &baseName, const QStringList &suffixes,
837 const QString &prefix)
838 {
839 QStringList searchPaths = filePluginPath;
840 bool qmldirPluginPathIsRelative = QDir::isRelativePath(qmldirPluginPath);
841 if (!qmldirPluginPathIsRelative)
842 searchPaths.prepend(qmldirPluginPath);
843
844 foreach (const QString &pluginPath, searchPaths) {
845
846 QString resolvedPath;
847
848 if (pluginPath == QLatin1String(".")) {
849 if (qmldirPluginPathIsRelative)
850 resolvedPath = qmldirPath.absoluteFilePath(qmldirPluginPath);
851 else
852 resolvedPath = qmldirPath.absolutePath();
853 } else {
854 resolvedPath = pluginPath;
855 }
856
857 // hack for resources, should probably go away
858 if (resolvedPath.startsWith(QLatin1Char(':')))
859 resolvedPath = QCoreApplication::applicationDirPath();
860
861 QDir dir(resolvedPath);
862 foreach (const QString &suffix, suffixes) {
863 QString pluginFileName = prefix;
864
865 pluginFileName += baseName;
866 pluginFileName += suffix;
867
868 QFileInfo fileInfo(dir, pluginFileName);
869
870 if (fileInfo.exists())
871 return fileInfo.absoluteFilePath();
872 }
873 }
874
875 if (qmlImportTrace())
876 qDebug() << "QDeclarativeImportDatabase::resolvePlugin: Could not resolve plugin" << baseName
877 << "in" << qmldirPath.absolutePath();
878
879 return QString();
880 }
881
882 /*!
883 \internal
884
885 Returns the result of the merge of \a baseName with \a dir and the platform suffix.
886
887 \table
888 \header \i Platform \i Valid suffixes
889 \row \i Windows \i \c .dll
890 \row \i Unix/Linux \i \c .so
891 \row \i AIX \i \c .a
892 \row \i HP-UX \i \c .sl, \c .so (HP-UXi)
893 \row \i Mac OS X \i \c .dylib, \c .bundle, \c .so
894 \row \i Symbian \i \c .dll
895 \endtable
896
897 Version number on unix are ignored.
898 */
resolvePlugin(const QDir & qmldirPath,const QString & qmldirPluginPath,const QString & baseName)899 QString QDeclarativeImportDatabase::resolvePlugin(const QDir &qmldirPath, const QString &qmldirPluginPath,
900 const QString &baseName)
901 {
902 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
903 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
904 QStringList()
905 # ifdef QT_DEBUG
906 << QLatin1String("d.dll") // try a qmake-style debug build first
907 # endif
908 << QLatin1String(".dll"));
909 #elif defined(Q_OS_SYMBIAN)
910 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
911 QStringList()
912 << QLatin1String(".dll")
913 << QLatin1String(".qtplugin"));
914 #else
915
916 # if defined(Q_OS_DARWIN)
917
918 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName,
919 QStringList()
920 # ifdef QT_DEBUG
921 << QLatin1String("_debug.dylib") // try a qmake-style debug build first
922 << QLatin1String(".dylib")
923 # else
924 << QLatin1String(".dylib")
925 << QLatin1String("_debug.dylib") // try a qmake-style debug build after
926 # endif
927 << QLatin1String(".so")
928 << QLatin1String(".bundle"),
929 QLatin1String("lib"));
930 # else // Generic Unix
931 QStringList validSuffixList;
932
933 # if defined(Q_OS_HPUX)
934 /*
935 See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
936 "In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
937 the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
938 */
939 validSuffixList << QLatin1String(".sl");
940 # if defined __ia64
941 validSuffixList << QLatin1String(".so");
942 # endif
943 # elif defined(Q_OS_AIX)
944 validSuffixList << QLatin1String(".a") << QLatin1String(".so");
945 # elif defined(Q_OS_UNIX)
946 validSuffixList << QLatin1String(".so");
947 # endif
948
949 // Examples of valid library names:
950 // libfoo.so
951
952 return resolvePlugin(qmldirPath, qmldirPluginPath, baseName, validSuffixList, QLatin1String("lib"));
953 # endif
954
955 #endif
956 }
957
958 /*!
959 \internal
960 */
pluginPathList() const961 QStringList QDeclarativeImportDatabase::pluginPathList() const
962 {
963 return filePluginPath;
964 }
965
966 /*!
967 \internal
968 */
setPluginPathList(const QStringList & paths)969 void QDeclarativeImportDatabase::setPluginPathList(const QStringList &paths)
970 {
971 filePluginPath = paths;
972 }
973
974 /*!
975 \internal
976 */
addPluginPath(const QString & path)977 void QDeclarativeImportDatabase::addPluginPath(const QString& path)
978 {
979 if (qmlImportTrace())
980 qDebug().nospace() << "QDeclarativeImportDatabase::addPluginPath: " << path;
981
982 QUrl url = QUrl(path);
983 if (url.isRelative() || url.scheme() == QLatin1String("file")
984 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
985 QDir dir = QDir(path);
986 filePluginPath.prepend(dir.canonicalPath());
987 } else {
988 filePluginPath.prepend(path);
989 }
990 }
991
992 /*!
993 \internal
994 */
addImportPath(const QString & path)995 void QDeclarativeImportDatabase::addImportPath(const QString& path)
996 {
997 if (qmlImportTrace())
998 qDebug().nospace() << "QDeclarativeImportDatabase::addImportPath: " << path;
999
1000 if (path.isEmpty())
1001 return;
1002
1003 QUrl url = QUrl(path);
1004 QString cPath;
1005
1006 if (url.isRelative() || url.scheme() == QLatin1String("file")
1007 || (url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
1008 QDir dir = QDir(path);
1009 cPath = dir.canonicalPath();
1010 } else {
1011 cPath = path;
1012 cPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
1013 }
1014
1015 if (!cPath.isEmpty()
1016 && !fileImportPath.contains(cPath))
1017 fileImportPath.prepend(cPath);
1018 }
1019
1020 /*!
1021 \internal
1022 */
importPathList() const1023 QStringList QDeclarativeImportDatabase::importPathList() const
1024 {
1025 return fileImportPath;
1026 }
1027
1028 /*!
1029 \internal
1030 */
setImportPathList(const QStringList & paths)1031 void QDeclarativeImportDatabase::setImportPathList(const QStringList &paths)
1032 {
1033 fileImportPath = paths;
1034 }
1035
1036 /*!
1037 \internal
1038 */
importPlugin(const QString & filePath,const QString & uri,QString * errorString)1039 bool QDeclarativeImportDatabase::importPlugin(const QString &filePath, const QString &uri, QString *errorString)
1040 {
1041 if (qmlImportTrace())
1042 qDebug().nospace() << "QDeclarativeImportDatabase::importPlugin: " << uri << " from " << filePath;
1043
1044 #ifndef QT_NO_LIBRARY
1045 QFileInfo fileInfo(filePath);
1046 const QString absoluteFilePath = fileInfo.absoluteFilePath();
1047
1048 bool engineInitialized = initializedPlugins.contains(absoluteFilePath);
1049 bool typesRegistered = qmlEnginePluginsWithRegisteredTypes()->contains(absoluteFilePath);
1050
1051 if (typesRegistered) {
1052 Q_ASSERT_X(qmlEnginePluginsWithRegisteredTypes()->value(absoluteFilePath) == uri,
1053 "QDeclarativeImportDatabase::importExtension",
1054 "Internal error: Plugin imported previously with different uri");
1055 }
1056
1057 if (!engineInitialized || !typesRegistered) {
1058 if (!QDeclarative_isFileCaseCorrect(absoluteFilePath)) {
1059 if (errorString)
1060 *errorString = tr("File name case mismatch for \"%1\"").arg(absoluteFilePath);
1061 return false;
1062 }
1063 QPluginLoader loader(absoluteFilePath);
1064
1065 if (!loader.load()) {
1066 if (errorString)
1067 *errorString = loader.errorString();
1068 return false;
1069 }
1070
1071 if (QDeclarativeExtensionInterface *iface = qobject_cast<QDeclarativeExtensionInterface *>(loader.instance())) {
1072
1073 const QByteArray bytes = uri.toUtf8();
1074 const char *moduleId = bytes.constData();
1075 if (!typesRegistered) {
1076
1077 // ### this code should probably be protected with a mutex.
1078 qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
1079 iface->registerTypes(moduleId);
1080 }
1081 if (!engineInitialized) {
1082 // things on the engine (eg. adding new global objects) have to be done for every engine.
1083
1084 // protect against double initialization
1085 initializedPlugins.insert(absoluteFilePath);
1086 iface->initializeEngine(engine, moduleId);
1087 }
1088 } else {
1089 if (errorString)
1090 *errorString = loader.errorString();
1091 return false;
1092 }
1093 }
1094
1095 return true;
1096 #else
1097 return false;
1098 #endif
1099 }
1100
1101
1102 QT_END_NAMESPACE
1103