Add missing QT_{BEGIN,END}_NAMESPACE
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmltypeloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmltypeloader_p.h"
43
44 #include <private/qqmlengine_p.h>
45 #include <private/qqmlglobal_p.h>
46 #include <private/qqmlthread_p.h>
47 #include <private/qqmlcompiler_p.h>
48 #include <private/qqmlcomponent_p.h>
49 #include <private/qqmlprofilerservice_p.h>
50 #include <private/qqmlmemoryprofiler_p.h>
51
52 #include <QtCore/qdir.h>
53 #include <QtCore/qfile.h>
54 #include <QtCore/qdebug.h>
55 #include <QtCore/qmutex.h>
56 #include <QtCore/qthread.h>
57 #include <QtQml/qqmlfile.h>
58 #include <QtCore/qdiriterator.h>
59 #include <QtQml/qqmlcomponent.h>
60 #include <QtCore/qwaitcondition.h>
61 #include <QtQml/qqmlextensioninterface.h>
62
63 #if defined (Q_OS_UNIX)
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <unistd.h>
67 #endif
68
69 #if defined (QT_LINUXBASE)
70 // LSB doesn't declare NAME_MAX. Use SYMLINK_MAX instead, which seems to
71 // always be identical to NAME_MAX
72 #ifndef NAME_MAX
73 #  define NAME_MAX _POSIX_SYMLINK_MAX
74 #endif
75
76 // LSB has a broken version of offsetof that can't be used at compile time
77 // https://lsbbugs.linuxfoundation.org/show_bug.cgi?id=3462
78 #undef offsetof
79 #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
80 #endif
81
82 // #define DATABLOB_DEBUG
83
84 #ifdef DATABLOB_DEBUG
85
86 #define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in main thread"); } while(false)
87 #define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QQmlDataLoader: Caller not in load thread"); } while(false)
88 #define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while(false)
89
90 #else
91
92 #define ASSERT_MAINTHREAD() 
93 #define ASSERT_LOADTHREAD()
94 #define ASSERT_CALLBACK()
95
96 #endif
97
98 DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
99
100 QT_BEGIN_NAMESPACE
101
102 namespace {
103
104     template<typename LockType>
105     struct LockHolder
106     {
107         LockType& lock;
108         LockHolder(LockType *l) : lock(*l) { lock.lock(); }
109         ~LockHolder() { lock.unlock(); }
110     };
111 }
112
113 // This is a lame object that we need to ensure that slots connected to
114 // QNetworkReply get called in the correct thread (the loader thread).  
115 // As QQmlDataLoader lives in the main thread, and we can't use
116 // Qt::DirectConnection connections from a QNetworkReply (because then 
117 // sender() wont work), we need to insert this object in the middle.
118 class QQmlDataLoaderNetworkReplyProxy : public QObject
119 {
120     Q_OBJECT
121 public:
122     QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l);
123
124 public slots:
125     void finished();
126     void downloadProgress(qint64, qint64);
127
128 private:
129     QQmlDataLoader *l;
130 };
131
132 class QQmlDataLoaderThread : public QQmlThread
133 {
134     typedef QQmlDataLoaderThread This;
135
136 public:
137     QQmlDataLoaderThread(QQmlDataLoader *loader);
138     QNetworkAccessManager *networkAccessManager() const;
139     QQmlDataLoaderNetworkReplyProxy *networkReplyProxy() const;
140
141     void load(QQmlDataBlob *b);
142     void loadAsync(QQmlDataBlob *b);
143     void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
144     void loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &);
145     void callCompleted(QQmlDataBlob *b);
146     void callDownloadProgressChanged(QQmlDataBlob *b, qreal p);
147     void initializeEngine(QQmlExtensionInterface *, const char *);
148
149 protected:
150     virtual void shutdownThread();
151
152 private:
153     void loadThread(QQmlDataBlob *b);
154     void loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &);
155     void callCompletedMain(QQmlDataBlob *b);
156     void callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p);
157     void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
158
159     QQmlDataLoader *m_loader;
160     mutable QNetworkAccessManager *m_networkAccessManager;
161     mutable QQmlDataLoaderNetworkReplyProxy *m_networkReplyProxy;
162 };
163
164
165 QQmlDataLoaderNetworkReplyProxy::QQmlDataLoaderNetworkReplyProxy(QQmlDataLoader *l) 
166 : l(l) 
167 {
168 }
169
170 void QQmlDataLoaderNetworkReplyProxy::finished()
171 {
172     Q_ASSERT(sender());
173     Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
174     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
175     l->networkReplyFinished(reply);
176 }
177
178 void QQmlDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
179 {
180     Q_ASSERT(sender());
181     Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
182     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
183     l->networkReplyProgress(reply, bytesReceived, bytesTotal);
184 }
185
186
187 /*!
188 \class QQmlDataBlob
189 \brief The QQmlDataBlob encapsulates a data request that can be issued to a QQmlDataLoader.
190 \internal
191
192 QQmlDataBlob's are loaded by a QQmlDataLoader.  The user creates the QQmlDataBlob
193 and then calls QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() to load it.
194 The QQmlDataLoader invokes callbacks on the QQmlDataBlob as data becomes available.
195 */
196
197 /*!
198 \enum QQmlDataBlob::Status
199
200 This enum describes the status of the data blob.
201
202 \list
203 \li Null The blob has not yet been loaded by a QQmlDataLoader
204 \li Loading The blob is loading network data.  The QQmlDataBlob::setData() callback has not yet been
205 invoked or has not yet returned.
206 \li WaitingForDependencies The blob is waiting for dependencies to be done before continueing.  This status
207 only occurs after the QQmlDataBlob::setData() callback has been made, and when the blob has outstanding
208 dependencies.
209 \li Complete The blob's data has been loaded and all dependencies are done.
210 \li Error An error has been set on this blob.
211 \endlist
212 */
213
214 /*!
215 \enum QQmlDataBlob::Type
216
217 This enum describes the type of the data blob.
218
219 \list
220 \li QmlFile This is a QQmlTypeData
221 \li JavaScriptFile This is a QQmlScriptData
222 \li QmldirFile This is a QQmlQmldirData
223 \endlist
224 */
225
226 /*!
227 Create a new QQmlDataBlob for \a url and of the provided \a type.
228 */
229 QQmlDataBlob::QQmlDataBlob(const QUrl &url, Type type)
230 : m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0), 
231   m_inCallback(false), m_isDone(false)
232 {
233 }
234
235 /*!  \internal */
236 QQmlDataBlob::~QQmlDataBlob()
237 {
238     Q_ASSERT(m_waitingOnMe.isEmpty());
239
240     cancelAllWaitingFor();
241 }
242
243 /*!
244 Returns the type provided to the constructor.
245 */
246 QQmlDataBlob::Type QQmlDataBlob::type() const
247 {
248     return m_type;
249 }
250
251 /*!
252 Returns the blob's status.
253 */
254 QQmlDataBlob::Status QQmlDataBlob::status() const
255 {
256     return m_data.status();
257 }
258
259 /*!
260 Returns true if the status is Null.
261 */
262 bool QQmlDataBlob::isNull() const
263 {
264     return status() == Null;
265 }
266
267 /*!
268 Returns true if the status is Loading.
269 */
270 bool QQmlDataBlob::isLoading() const
271 {
272     return status() == Loading;
273 }
274
275 /*!
276 Returns true if the status is WaitingForDependencies.
277 */
278 bool QQmlDataBlob::isWaiting() const
279 {
280     return status() == WaitingForDependencies;
281 }
282
283 /*!
284 Returns true if the status is Complete.
285 */
286 bool QQmlDataBlob::isComplete() const
287 {
288     return status() == Complete;
289 }
290
291 /*!
292 Returns true if the status is Error.
293 */
294 bool QQmlDataBlob::isError() const
295 {
296     return status() == Error;
297 }
298
299 /*!
300 Returns true if the status is Complete or Error.
301 */
302 bool QQmlDataBlob::isCompleteOrError() const
303 {
304     Status s = status();
305     return s == Error || s == Complete;
306 }
307
308 /*!
309 Returns the data download progress from 0 to 1.
310 */
311 qreal QQmlDataBlob::progress() const
312 {
313     quint8 p = m_data.progress();
314     if (p == 0xFF) return 1.;
315     else return qreal(p) / qreal(0xFF);
316 }
317
318 /*!
319 Returns the blob url passed to the constructor.  If a network redirect
320 happens while fetching the data, this url remains the same.
321
322 \sa finalUrl()
323 */
324 QUrl QQmlDataBlob::url() const
325 {
326     return m_url;
327 }
328
329 /*!
330 Returns the final url of the data.  Initially this is the same as
331 url(), but if a network redirect happens while fetching the data, this url
332 is updated to reflect the new location.
333
334 May only be called from the load thread, or after the blob isCompleteOrError().
335 */
336 QUrl QQmlDataBlob::finalUrl() const
337 {
338     Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
339     return m_finalUrl;
340 }
341
342 /*!
343 Returns the finalUrl() as a string.
344 */
345 QString QQmlDataBlob::finalUrlString() const
346 {
347     Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
348     if (m_finalUrlString.isEmpty())
349         m_finalUrlString = m_finalUrl.toString();
350
351     return m_finalUrlString;
352 }
353
354 /*!
355 Return the errors on this blob.
356
357 May only be called from the load thread, or after the blob isCompleteOrError().
358 */
359 QList<QQmlError> QQmlDataBlob::errors() const
360 {
361     Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
362     return m_errors;
363 }
364
365 /*!
366 Mark this blob as having \a errors.
367
368 All outstanding dependencies will be cancelled.  Requests to add new dependencies 
369 will be ignored.  Entry into the Error state is irreversable.
370
371 The setError() method may only be called from within a QQmlDataBlob callback.
372 */
373 void QQmlDataBlob::setError(const QQmlError &errors)
374 {
375     ASSERT_CALLBACK();
376
377     QList<QQmlError> l;
378     l << errors;
379     setError(l);
380 }
381
382 /*!
383 \overload
384 */
385 void QQmlDataBlob::setError(const QList<QQmlError> &errors)
386 {
387     ASSERT_CALLBACK();
388
389     Q_ASSERT(status() != Error);
390     Q_ASSERT(m_errors.isEmpty());
391
392     m_errors = errors; // Must be set before the m_data fence
393     m_data.setStatus(Error);
394
395     if (dumpErrors()) {
396         qWarning().nospace() << "Errors for " << m_finalUrl.toString();
397         for (int ii = 0; ii < errors.count(); ++ii)
398             qWarning().nospace() << "    " << qPrintable(errors.at(ii).toString());
399     }
400     cancelAllWaitingFor();
401
402     if (!m_inCallback)
403         tryDone();
404 }
405
406 /*! 
407 Wait for \a blob to become complete or to error.  If \a blob is already 
408 complete or in error, or this blob is already complete, this has no effect.
409
410 The setError() method may only be called from within a QQmlDataBlob callback.
411 */
412 void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
413 {
414     ASSERT_CALLBACK();
415
416     Q_ASSERT(status() != Null);
417
418     if (!blob ||
419         blob->status() == Error || blob->status() == Complete ||
420         status() == Error || status() == Complete || m_isDone || 
421         m_waitingFor.contains(blob))
422         return;
423
424     blob->addref();
425
426     m_data.setStatus(WaitingForDependencies);
427
428     m_waitingFor.append(blob);
429     blob->m_waitingOnMe.append(this);
430 }
431
432 /*!
433 \fn void QQmlDataBlob::dataReceived(const Data &data)
434
435 Invoked when data for the blob is received.  Implementors should use this callback
436 to determine a blob's dependencies.  Within this callback you may call setError()
437 or addDependency().
438 */
439
440 /*!
441 Invoked once data has either been received or a network error occurred, and all 
442 dependencies are complete.
443
444 You can set an error in this method, but you cannot add new dependencies.  Implementors
445 should use this callback to finalize processing of data.
446
447 The default implementation does nothing.
448
449 XXX Rename processData() or some such to avoid confusion between done() (processing thread)
450 and completed() (main thread)
451 */
452 void QQmlDataBlob::done()
453 {
454 }
455
456 /*!
457 Invoked if there is a network error while fetching this blob.
458
459 The default implementation sets an appropriate QQmlError.
460 */
461 void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
462 {
463     Q_UNUSED(networkError);
464
465     QQmlError error;
466     error.setUrl(m_finalUrl);
467
468     const char *errorString = 0;
469     switch (networkError) {
470         default:
471             errorString = "Network error";
472             break;
473         case QNetworkReply::ConnectionRefusedError:
474             errorString = "Connection refused";
475             break;
476         case QNetworkReply::RemoteHostClosedError:
477             errorString = "Remote host closed the connection";
478             break;
479         case QNetworkReply::HostNotFoundError:
480             errorString = "Host not found";
481             break;
482         case QNetworkReply::TimeoutError:
483             errorString = "Timeout";
484             break;
485         case QNetworkReply::ProxyConnectionRefusedError:
486         case QNetworkReply::ProxyConnectionClosedError:
487         case QNetworkReply::ProxyNotFoundError:
488         case QNetworkReply::ProxyTimeoutError:
489         case QNetworkReply::ProxyAuthenticationRequiredError:
490         case QNetworkReply::UnknownProxyError:
491             errorString = "Proxy error";
492             break;
493         case QNetworkReply::ContentAccessDenied:
494             errorString = "Access denied";
495             break;
496         case QNetworkReply::ContentNotFoundError:
497             errorString = "File not found";
498             break;
499         case QNetworkReply::AuthenticationRequiredError:
500             errorString = "Authentication required";
501             break;
502     };
503
504     error.setDescription(QLatin1String(errorString));
505
506     setError(error);
507 }
508
509 /*! 
510 Called if \a blob, which was previously waited for, has an error.
511
512 The default implementation does nothing.
513 */
514 void QQmlDataBlob::dependencyError(QQmlDataBlob *blob)
515 {
516     Q_UNUSED(blob);
517 }
518
519 /*!
520 Called if \a blob, which was previously waited for, has completed.
521
522 The default implementation does nothing.
523 */
524 void QQmlDataBlob::dependencyComplete(QQmlDataBlob *blob)
525 {
526     Q_UNUSED(blob);
527 }
528
529 /*! 
530 Called when all blobs waited for have completed.  This occurs regardless of 
531 whether they are in error, or complete state.  
532
533 The default implementation does nothing.
534 */
535 void QQmlDataBlob::allDependenciesDone()
536 {
537 }
538
539 /*!
540 Called when the download progress of this blob changes.  \a progress goes
541 from 0 to 1.
542
543 This callback is only invoked if an asynchronous load for this blob is 
544 made.  An asynchronous load is one in which the Asynchronous mode is
545 specified explicitly, or one that is implicitly delayed due to a network 
546 operation.
547
548 The default implementation does nothing.
549 */
550 void QQmlDataBlob::downloadProgressChanged(qreal progress)
551 {
552     Q_UNUSED(progress);
553 }
554
555 /*!
556 Invoked on the main thread sometime after done() was called on the load thread.
557
558 You cannot modify the blobs state at all in this callback and cannot depend on the
559 order or timeliness of these callbacks.  Implementors should use this callback to notify 
560 dependencies on the main thread that the blob is done and not a lot else.
561
562 This callback is only invoked if an asynchronous load for this blob is 
563 made.  An asynchronous load is one in which the Asynchronous mode is
564 specified explicitly, or one that is implicitly delayed due to a network 
565 operation.
566
567 The default implementation does nothing.
568 */
569 void QQmlDataBlob::completed()
570 {
571 }
572
573
574 void QQmlDataBlob::tryDone()
575 {
576     if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
577         m_isDone = true;
578         addref();
579
580 #ifdef DATABLOB_DEBUG
581         qWarning("QQmlDataBlob::done() %s", qPrintable(url().toString()));
582 #endif
583         done();
584
585         if (status() != Error)
586             m_data.setStatus(Complete);
587
588         notifyAllWaitingOnMe();
589
590         // Locking is not required here, as anyone expecting callbacks must
591         // already be protected against the blob being completed (as set above);
592         if (m_data.isAsync()) {
593 #ifdef DATABLOB_DEBUG
594             qWarning("QQmlDataBlob: Dispatching completed");
595 #endif
596             m_manager->m_thread->callCompleted(this);
597         }
598
599         release();
600     }
601 }
602
603 void QQmlDataBlob::cancelAllWaitingFor()
604 {
605     while (m_waitingFor.count()) {
606         QQmlDataBlob *blob = m_waitingFor.takeLast();
607
608         Q_ASSERT(blob->m_waitingOnMe.contains(this));
609
610         blob->m_waitingOnMe.removeOne(this);
611
612         blob->release();
613     }
614 }
615
616 void QQmlDataBlob::notifyAllWaitingOnMe()
617 {
618     while (m_waitingOnMe.count()) {
619         QQmlDataBlob *blob = m_waitingOnMe.takeLast();
620
621         Q_ASSERT(blob->m_waitingFor.contains(this));
622
623         blob->notifyComplete(this);
624     }
625 }
626
627 void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
628 {
629     Q_ASSERT(m_waitingFor.contains(blob));
630     Q_ASSERT(blob->status() == Error || blob->status() == Complete);
631
632     m_inCallback = true;
633
634     m_waitingFor.removeOne(blob);
635
636     if (blob->status() == Error) {
637         dependencyError(blob);
638     } else if (blob->status() == Complete) {
639         dependencyComplete(blob);
640     }
641
642     blob->release();
643
644     if (!isError() && m_waitingFor.isEmpty()) 
645         allDependenciesDone();
646
647     m_inCallback = false;
648
649     tryDone();
650 }
651
652 #define TD_STATUS_MASK 0x0000FFFF
653 #define TD_STATUS_SHIFT 0
654 #define TD_PROGRESS_MASK 0x00FF0000
655 #define TD_PROGRESS_SHIFT 16
656 #define TD_ASYNC_MASK 0x80000000
657
658 QQmlDataBlob::ThreadData::ThreadData()
659 : _p(0)
660 {
661 }
662
663 QQmlDataBlob::Status QQmlDataBlob::ThreadData::status() const
664 {
665     return QQmlDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
666 }
667
668 void QQmlDataBlob::ThreadData::setStatus(QQmlDataBlob::Status status)
669 {
670     while (true) {
671         int d = _p.load();
672         int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
673         if (d == nd || _p.testAndSetOrdered(d, nd)) return;
674     }
675 }
676
677 bool QQmlDataBlob::ThreadData::isAsync() const
678 {
679     return _p.load() & TD_ASYNC_MASK;
680 }
681
682 void QQmlDataBlob::ThreadData::setIsAsync(bool v)
683 {
684     while (true) {
685         int d = _p.load();
686         int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
687         if (d == nd || _p.testAndSetOrdered(d, nd)) return;
688     }
689 }
690
691 quint8 QQmlDataBlob::ThreadData::progress() const
692 {
693     return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
694 }
695
696 void QQmlDataBlob::ThreadData::setProgress(quint8 v)
697 {
698     while (true) {
699         int d = _p.load();
700         int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
701         if (d == nd || _p.testAndSetOrdered(d, nd)) return;
702     }
703 }
704
705 QQmlDataLoaderThread::QQmlDataLoaderThread(QQmlDataLoader *loader)
706 : m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
707 {
708 }
709
710 QNetworkAccessManager *QQmlDataLoaderThread::networkAccessManager() const
711 {
712     Q_ASSERT(isThisThread());
713     if (!m_networkAccessManager) {
714         m_networkAccessManager = QQmlEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0);
715         m_networkReplyProxy = new QQmlDataLoaderNetworkReplyProxy(m_loader);
716     }
717
718     return m_networkAccessManager;
719 }
720
721 QQmlDataLoaderNetworkReplyProxy *QQmlDataLoaderThread::networkReplyProxy() const
722 {
723     Q_ASSERT(isThisThread());
724     Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
725     return m_networkReplyProxy;
726 }
727
728 void QQmlDataLoaderThread::load(QQmlDataBlob *b) 
729
730     b->addref();
731     callMethodInThread(&This::loadThread, b); 
732 }
733
734 void QQmlDataLoaderThread::loadAsync(QQmlDataBlob *b)
735 {
736     b->addref();
737     postMethodToThread(&This::loadThread, b);
738 }
739
740 void QQmlDataLoaderThread::loadWithStaticData(QQmlDataBlob *b, const QByteArray &d)
741 {
742     b->addref();
743     callMethodInThread(&This::loadWithStaticDataThread, b, d);
744 }
745
746 void QQmlDataLoaderThread::loadWithStaticDataAsync(QQmlDataBlob *b, const QByteArray &d)
747 {
748     b->addref();
749     postMethodToThread(&This::loadWithStaticDataThread, b, d);
750 }
751
752 void QQmlDataLoaderThread::callCompleted(QQmlDataBlob *b) 
753
754     b->addref(); 
755     postMethodToMain(&This::callCompletedMain, b); 
756 }
757
758 void QQmlDataLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p) 
759
760     b->addref(); 
761     postMethodToMain(&This::callDownloadProgressChangedMain, b, p); 
762 }
763
764 void QQmlDataLoaderThread::initializeEngine(QQmlExtensionInterface *iface, 
765                                                     const char *uri)
766 {
767     callMethodInMain(&This::initializeEngineMain, iface, uri);
768 }
769
770 void QQmlDataLoaderThread::shutdownThread()
771 {
772     delete m_networkAccessManager;
773     m_networkAccessManager = 0;
774     delete m_networkReplyProxy;
775     m_networkReplyProxy = 0;
776 }
777
778 void QQmlDataLoaderThread::loadThread(QQmlDataBlob *b) 
779
780     m_loader->loadThread(b); 
781     b->release();
782 }
783
784 void QQmlDataLoaderThread::loadWithStaticDataThread(QQmlDataBlob *b, const QByteArray &d)
785 {
786     m_loader->loadWithStaticDataThread(b, d);
787     b->release();
788 }
789
790 void QQmlDataLoaderThread::callCompletedMain(QQmlDataBlob *b) 
791
792     QML_MEMORY_SCOPE_URL(b->url());
793 #ifdef DATABLOB_DEBUG
794     qWarning("QQmlDataLoaderThread: %s completed() callback", qPrintable(b->url().toString())); 
795 #endif
796     b->completed(); 
797     b->release(); 
798 }
799
800 void QQmlDataLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qreal p) 
801
802 #ifdef DATABLOB_DEBUG
803     qWarning("QQmlDataLoaderThread: %s downloadProgressChanged(%f) callback", 
804              qPrintable(b->url().toString()), p); 
805 #endif
806     b->downloadProgressChanged(p); 
807     b->release(); 
808 }
809
810 void QQmlDataLoaderThread::initializeEngineMain(QQmlExtensionInterface *iface, 
811                                                         const char *uri)
812 {
813     Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
814     iface->initializeEngine(m_loader->engine(), uri);
815 }
816
817 /*!
818 \class QQmlDataLoader
819 \brief The QQmlDataLoader class abstracts loading files and their dependencies over the network.
820 \internal
821
822 The QQmlDataLoader class is provided for the exclusive use of the QQmlTypeLoader class.
823
824 Clients create QQmlDataBlob instances and submit them to the QQmlDataLoader class
825 through the QQmlDataLoader::load() or QQmlDataLoader::loadWithStaticData() methods.
826 The loader then fetches the data over the network or from the local file system in an efficient way.
827 QQmlDataBlob is an abstract class, so should always be specialized.
828
829 Once data is received, the QQmlDataBlob::dataReceived() method is invoked on the blob.  The
830 derived class should use this callback to process the received data.  Processing of the data can 
831 result in an error being set (QQmlDataBlob::setError()), or one or more dependencies being
832 created (QQmlDataBlob::addDependency()).  Dependencies are other QQmlDataBlob's that
833 are required before processing can fully complete.
834
835 To complete processing, the QQmlDataBlob::done() callback is invoked.  done() is called when
836 one of these three preconditions are met.
837
838 \list 1
839 \li The QQmlDataBlob has no dependencies.
840 \li The QQmlDataBlob has an error set.
841 \li All the QQmlDataBlob's dependencies are themselves "done()".
842 \endlist
843
844 Thus QQmlDataBlob::done() will always eventually be called, even if the blob has an error set.
845 */
846
847 /*!
848 Create a new QQmlDataLoader for \a engine.
849 */
850 QQmlDataLoader::QQmlDataLoader(QQmlEngine *engine)
851 : m_engine(engine), m_thread(new QQmlDataLoaderThread(this))
852 {
853 }
854
855 /*! \internal */
856 QQmlDataLoader::~QQmlDataLoader()
857 {
858     for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) 
859         (*iter)->release();
860
861     shutdownThread();
862     delete m_thread;
863 }
864
865 void QQmlDataLoader::lock()
866 {
867     m_thread->lock();
868 }
869
870 void QQmlDataLoader::unlock()
871 {
872     m_thread->unlock();
873 }
874
875 /*!
876 Load the provided \a blob from the network or filesystem.
877
878 The loader must be locked.
879 */
880 void QQmlDataLoader::load(QQmlDataBlob *blob, Mode mode)
881 {
882 #ifdef DATABLOB_DEBUG
883     qWarning("QQmlDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()), 
884              m_thread->isThisThread()?"Compile":"Engine");
885 #endif
886
887     Q_ASSERT(blob->status() == QQmlDataBlob::Null);
888     Q_ASSERT(blob->m_manager == 0);
889
890     blob->m_data.setStatus(QQmlDataBlob::Loading);
891     blob->m_manager = this;
892
893     if (m_thread->isThisThread()) {
894         unlock();
895         loadThread(blob);
896         lock();
897     } else if (mode == PreferSynchronous) {
898         unlock();
899         m_thread->load(blob);
900         lock();
901         if (!blob->isCompleteOrError())
902             blob->m_data.setIsAsync(true);
903     } else {
904         Q_ASSERT(mode == Asynchronous);
905         blob->m_data.setIsAsync(true);
906         unlock();
907         m_thread->loadAsync(blob);
908         lock();
909     }
910 }
911
912 /*!
913 Load the provided \a blob with \a data.  The blob's URL is not used by the data loader in this case.
914
915 The loader must be locked.
916 */
917 void QQmlDataLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &data, Mode mode)
918 {
919 #ifdef DATABLOB_DEBUG
920     qWarning("QQmlDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()), 
921              m_thread->isThisThread()?"Compile":"Engine");
922 #endif
923
924     Q_ASSERT(blob->status() == QQmlDataBlob::Null);
925     Q_ASSERT(blob->m_manager == 0);
926     
927     blob->m_data.setStatus(QQmlDataBlob::Loading);
928     blob->m_manager = this;
929
930     if (m_thread->isThisThread()) {
931         unlock();
932         loadWithStaticDataThread(blob, data);
933         lock();
934     } else if (mode == PreferSynchronous) {
935         unlock();
936         m_thread->loadWithStaticData(blob, data);
937         lock();
938         if (!blob->isCompleteOrError())
939             blob->m_data.setIsAsync(true);
940     } else {
941         Q_ASSERT(mode == Asynchronous);
942         blob->m_data.setIsAsync(true);
943         unlock();
944         m_thread->loadWithStaticDataAsync(blob, data);
945         lock();
946     }
947 }
948
949 void QQmlDataLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data)
950 {
951     ASSERT_LOADTHREAD();
952
953     setData(blob, data);
954 }
955
956 void QQmlDataLoader::loadThread(QQmlDataBlob *blob)
957 {
958     ASSERT_LOADTHREAD();
959
960     // Don't continue loading if we've been shutdown
961     if (m_thread->isShutdown()) {
962         QQmlError error;
963         error.setDescription(QLatin1String("Interrupted by shutdown"));
964         blob->setError(error);
965         return;
966     }
967
968     if (blob->m_url.isEmpty()) {
969         QQmlError error;
970         error.setDescription(QLatin1String("Invalid null URL"));
971         blob->setError(error);
972         return;
973     }
974
975     QML_MEMORY_SCOPE_URL(blob->m_url);
976     QQmlEnginePrivate *engine_d = QQmlEnginePrivate::get(m_engine);
977     QHash<QUrl, QByteArray> debugCache = engine_d->debugChangesCache();
978
979     if (!debugCache.isEmpty()) {
980         foreach (const QUrl &url, debugCache.keys()) {
981             if (blob->m_url == blob->m_url.resolved(url)) {
982                 blob->m_data.setProgress(0xFF);
983                 if (blob->m_data.isAsync())
984                     m_thread->callDownloadProgressChanged(blob, 1.);
985                 setData(blob, debugCache.value(url, QByteArray()));
986                 return;
987             }
988         }
989     }
990
991     if (QQmlFile::isSynchronous(blob->m_url)) {
992         QQmlFile file(m_engine, blob->m_url);
993
994         if (file.isError()) {
995             QQmlError error;
996             error.setUrl(blob->m_url);
997             error.setDescription(file.error());
998             blob->setError(error);
999             return;
1000         }
1001
1002         blob->m_data.setProgress(0xFF);
1003         if (blob->m_data.isAsync())
1004             m_thread->callDownloadProgressChanged(blob, 1.);
1005
1006         setData(blob, &file);
1007
1008     } else {
1009
1010         QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
1011         QObject *nrp = m_thread->networkReplyProxy();
1012         QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), 
1013                          nrp, SLOT(downloadProgress(qint64,qint64)));
1014         QObject::connect(reply, SIGNAL(finished()), 
1015                          nrp, SLOT(finished()));
1016         m_networkReplies.insert(reply, blob);
1017 #ifdef DATABLOB_DEBUG
1018         qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString()));
1019 #endif
1020
1021         blob->addref();
1022     }
1023 }
1024
1025 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
1026
1027 void QQmlDataLoader::networkReplyFinished(QNetworkReply *reply)
1028 {
1029     Q_ASSERT(m_thread->isThisThread());
1030
1031     reply->deleteLater();
1032
1033     QQmlDataBlob *blob = m_networkReplies.take(reply);
1034
1035     Q_ASSERT(blob);
1036
1037     blob->m_redirectCount++;
1038
1039     if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
1040         QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1041         if (redirect.isValid()) {
1042             QUrl url = reply->url().resolved(redirect.toUrl());
1043             blob->m_finalUrl = url;
1044
1045             QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url));
1046             QObject *nrp = m_thread->networkReplyProxy();
1047             QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished()));
1048             m_networkReplies.insert(reply, blob);
1049 #ifdef DATABLOB_DEBUG
1050             qWarning("QQmlDataBlob: redirected to %s", qPrintable(blob->m_finalUrl.toString()));
1051 #endif
1052             return;
1053         }
1054     }
1055
1056     if (reply->error()) {
1057         blob->networkError(reply->error());
1058     } else {
1059         QByteArray data = reply->readAll();
1060         setData(blob, data);
1061     }
1062
1063     blob->release();
1064 }
1065
1066 void QQmlDataLoader::networkReplyProgress(QNetworkReply *reply,
1067                                                   qint64 bytesReceived, qint64 bytesTotal)
1068 {
1069     Q_ASSERT(m_thread->isThisThread());
1070
1071     QQmlDataBlob *blob = m_networkReplies.value(reply);
1072
1073     Q_ASSERT(blob);
1074
1075     if (bytesTotal != 0) {
1076         quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal));
1077         blob->m_data.setProgress(progress);
1078         if (blob->m_data.isAsync())
1079             m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
1080     }
1081 }
1082
1083 /*!
1084 Return the QQmlEngine associated with this loader
1085 */
1086 QQmlEngine *QQmlDataLoader::engine() const
1087 {
1088     return m_engine;
1089 }
1090
1091 /*!
1092 Call the initializeEngine() method on \a iface.  Used by QQmlImportDatabase to ensure it
1093 gets called in the correct thread.
1094 */
1095 void QQmlDataLoader::initializeEngine(QQmlExtensionInterface *iface, 
1096                                               const char *uri)
1097 {
1098     Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
1099
1100     if (m_thread->isThisThread()) {
1101         m_thread->initializeEngine(iface, uri);
1102     } else {
1103         Q_ASSERT(engine()->thread() == QThread::currentThread());
1104         iface->initializeEngine(engine(), uri);
1105     }
1106 }
1107
1108
1109 void QQmlDataLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
1110 {
1111     QML_MEMORY_SCOPE_URL(blob->url());
1112     QQmlDataBlob::Data d;
1113     d.d = &data;
1114     setData(blob, d);
1115 }
1116
1117 void QQmlDataLoader::setData(QQmlDataBlob *blob, QQmlFile *file)
1118 {
1119     QML_MEMORY_SCOPE_URL(blob->url());
1120     QQmlDataBlob::Data d;
1121     d.d = file;
1122     setData(blob, d);
1123 }
1124
1125 void QQmlDataLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::Data &d)
1126 {
1127     QML_MEMORY_SCOPE_URL(blob->url());
1128     blob->m_inCallback = true;
1129
1130     blob->dataReceived(d);
1131
1132     if (!blob->isError() && !blob->isWaiting())
1133         blob->allDependenciesDone();
1134
1135     if (blob->status() != QQmlDataBlob::Error) 
1136         blob->m_data.setStatus(QQmlDataBlob::WaitingForDependencies);
1137
1138     blob->m_inCallback = false;
1139
1140     blob->tryDone();
1141 }
1142
1143 void QQmlDataLoader::shutdownThread()
1144 {
1145     if (!m_thread->isShutdown())
1146         m_thread->shutdown();
1147 }
1148
1149 QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
1150 : QQmlDataBlob(url, type), m_typeLoader(loader), m_imports(loader)
1151 {
1152 }
1153
1154 QQmlTypeLoader::Blob::~Blob()
1155 {
1156     for (int ii = 0; ii < m_qmldirs.count(); ++ii)
1157         m_qmldirs.at(ii)->release();
1158 }
1159
1160 bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QQmlScript::Import *import, int priority, QList<QQmlError> *errors)
1161 {
1162     QQmlQmldirData *data = typeLoader()->getQmldir(url);
1163
1164     data->setImport(import);
1165     data->setPriority(priority);
1166
1167     if (data->status() == Error) {
1168         // This qmldir must not exist - which is not an error
1169         data->release();
1170         return true;
1171     } else if (data->status() == Complete) {
1172         // This data is already available
1173         return qmldirDataAvailable(data, errors);
1174     }
1175
1176     // Wait for this data to become available
1177     addDependency(data);
1178     return true;
1179 }
1180
1181 bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QQmlScript::Import *import, QList<QQmlError> *errors)
1182 {
1183     QString qmldirIdentifier = data->url().toString();
1184     QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1);
1185
1186     typeLoader()->setQmldirContent(qmldirIdentifier, data->content());
1187
1188     if (!m_imports.updateQmldirContent(typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors))
1189         return false;
1190
1191     QHash<const QQmlScript::Import *, int>::iterator it = m_unresolvedImports.find(import);
1192     if (it != m_unresolvedImports.end()) {
1193         *it = data->priority();
1194     }
1195
1196     // Release this reference at destruction
1197     m_qmldirs << data;
1198
1199     if (!import->qualifier.isEmpty()) {
1200         // Does this library contain any qualified scripts?
1201         QUrl libraryUrl(qmldirUrl);
1202         const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier, qmldirUrl);
1203         foreach (const QQmlDirParser::Script &script, qmldir->scripts()) {
1204             QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
1205             QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1206             addDependency(blob);
1207
1208             scriptImported(blob, import->location.start, script.nameSpace, import->qualifier);
1209         }
1210     }
1211
1212     return true;
1213 }
1214
1215 bool QQmlTypeLoader::Blob::addImport(const QQmlScript::Import &import, QList<QQmlError> *errors)
1216 {
1217     Q_ASSERT(errors);
1218
1219     QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
1220
1221     if (import.type == QQmlScript::Import::Script) {
1222         QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1223         QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1224         addDependency(blob);
1225
1226         scriptImported(blob, import.location.start, import.qualifier, QString());
1227     } else if (import.type == QQmlScript::Import::Library) {
1228         QString qmldirFilePath;
1229         QString qmldirUrl;
1230
1231         if (m_imports.locateQmldir(importDatabase, import.uri, import.majorVersion, import.minorVersion,
1232                                  &qmldirFilePath, &qmldirUrl)) {
1233             // This is a local library import
1234             if (!m_imports.addLibraryImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1235                                           import.minorVersion, qmldirFilePath, qmldirUrl, false, errors))
1236                 return false;
1237
1238             if (!import.qualifier.isEmpty()) {
1239                 // Does this library contain any qualified scripts?
1240                 QUrl libraryUrl(qmldirUrl);
1241                 const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath, qmldirUrl);
1242                 foreach (const QQmlDirParser::Script &script, qmldir->scripts()) {
1243                     QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
1244                     QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1245                     addDependency(blob);
1246
1247                     scriptImported(blob, import.location.start, script.nameSpace, import.qualifier);
1248                 }
1249             }
1250         } else {
1251             // Is this a module?
1252             if (QQmlMetaType::isAnyModule(import.uri)) {
1253                 if (!m_imports.addLibraryImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1254                                               import.minorVersion, QString(), QString(), false, errors))
1255                     return false;
1256             } else {
1257                 // We haven't yet resolved this import
1258                 m_unresolvedImports.insert(&import, 0);
1259
1260                 // Query any network import paths for this library
1261                 QStringList remotePathList = importDatabase->importPathList(QQmlImportDatabase::Remote);
1262                 if (!remotePathList.isEmpty()) {
1263                     // Add this library and request the possible locations for it
1264                     if (!m_imports.addLibraryImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1265                                                   import.minorVersion, QString(), QString(), true, errors))
1266                         return false;
1267
1268                     // Probe for all possible locations
1269                     int priority = 0;
1270                     for (int version = QQmlImports::FullyVersioned; version <= QQmlImports::Unversioned; ++version) {
1271                         foreach (const QString &path, remotePathList) {
1272                             QString qmldirUrl = QQmlImports::completeQmldirPath(import.uri, path, import.majorVersion, import.minorVersion,
1273                                                                                 static_cast<QQmlImports::ImportVersion>(version));
1274                             if (!fetchQmldir(QUrl(qmldirUrl), &import, ++priority, errors))
1275                                 return false;
1276                         }
1277                     }
1278                 }
1279             }
1280         }
1281     } else {
1282         Q_ASSERT(import.type == QQmlScript::Import::File);
1283
1284         bool incomplete = false;
1285
1286         QUrl qmldirUrl;
1287         if (import.qualifier.isEmpty()) {
1288             qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1289             if (!QQmlImports::isLocal(qmldirUrl)) {
1290                 // This is a remote file; the import is currently incomplete
1291                 incomplete = true;
1292             }
1293         }
1294
1295         if (!m_imports.addFileImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
1296                                    import.minorVersion, incomplete, errors))
1297             return false;
1298
1299         if (incomplete) {
1300             if (!fetchQmldir(qmldirUrl, &import, 1, errors))
1301                 return false;
1302         }
1303     }
1304
1305     return true;
1306 }
1307
1308 void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
1309 {
1310     if (blob->type() == QQmlDataBlob::QmldirFile) {
1311         QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
1312         data->release();
1313     }
1314 }
1315
1316 void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
1317 {
1318     if (blob->type() == QQmlDataBlob::QmldirFile) {
1319         QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
1320
1321         const QQmlScript::Import *import = data->import();
1322
1323         QList<QQmlError> errors;
1324         if (!qmldirDataAvailable(data, &errors)) {
1325             Q_ASSERT(errors.size());
1326             QQmlError error(errors.takeFirst());
1327             error.setUrl(m_imports.baseUrl());
1328             error.setLine(import->location.start.line);
1329             error.setColumn(import->location.start.column);
1330             errors.prepend(error); // put it back on the list after filling out information.
1331             setError(errors);
1332         }
1333     }
1334 }
1335
1336 bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors)
1337 {
1338     bool resolve = true;
1339
1340     const QQmlScript::Import *import = data->import();
1341     data->setImport(0);
1342
1343     int priority = data->priority();
1344     data->setPriority(0);
1345
1346     if (import) {
1347         // Do we need to resolve this import?
1348         QHash<const QQmlScript::Import *, int>::iterator it = m_unresolvedImports.find(import);
1349         if (it != m_unresolvedImports.end()) {
1350             resolve = (*it == 0) || (*it > priority);
1351         }
1352
1353         if (resolve) {
1354             // This is the (current) best resolution for this import
1355             if (!updateQmldir(data, import, errors)) {
1356                 data->release();
1357                 return false;
1358             }
1359
1360             *it = priority;
1361             return true;
1362         }
1363     }
1364
1365     data->release();
1366     return true;
1367 }
1368
1369
1370 QQmlTypeLoader::QmldirContent::QmldirContent()
1371 {
1372 }
1373
1374 bool QQmlTypeLoader::QmldirContent::hasError() const
1375 {
1376     return m_parser.hasError();
1377 }
1378
1379 QList<QQmlError> QQmlTypeLoader::QmldirContent::errors(const QString &uri) const
1380 {
1381     return m_parser.errors(uri);
1382 }
1383
1384 QString QQmlTypeLoader::QmldirContent::typeNamespace() const
1385 {
1386     return m_parser.typeNamespace();
1387 }
1388
1389 void QQmlTypeLoader::QmldirContent::setContent(const QString &location, const QString &content)
1390 {
1391     m_location = location;
1392     m_parser.parse(content);
1393 }
1394
1395 void QQmlTypeLoader::QmldirContent::setError(const QQmlError &error)
1396 {
1397     m_parser.setError(error);
1398 }
1399
1400 QQmlDirComponents QQmlTypeLoader::QmldirContent::components() const
1401 {
1402     return m_parser.components();
1403 }
1404
1405 QQmlDirScripts QQmlTypeLoader::QmldirContent::scripts() const
1406 {
1407     return m_parser.scripts();
1408 }
1409
1410 QQmlDirPlugins QQmlTypeLoader::QmldirContent::plugins() const
1411 {
1412     return m_parser.plugins();
1413 }
1414
1415 QString QQmlTypeLoader::QmldirContent::pluginLocation() const
1416 {
1417     return m_location;
1418 }
1419
1420
1421 /*!
1422 Constructs a new type loader that uses the given \a engine.
1423 */
1424 QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine)
1425 : QQmlDataLoader(engine)
1426 {
1427 }
1428
1429 /*!
1430 Destroys the type loader, first clearing the cache of any information about
1431 loaded files.
1432 */
1433 QQmlTypeLoader::~QQmlTypeLoader()
1434 {
1435     // Stop the loader thread before releasing resources
1436     shutdownThread();
1437
1438     clearCache();
1439 }
1440
1441 QQmlImportDatabase *QQmlTypeLoader::importDatabase()
1442 {
1443     return &QQmlEnginePrivate::get(engine())->importDatabase;
1444 }
1445
1446 /*!
1447 \enum QQmlTypeLoader::Option
1448
1449 This enum defines the options that control the way type data is handled.
1450
1451 \value None             The default value, indicating that no other options
1452                         are enabled.
1453 \value PreserveParser   The parser used to handle the type data is preserved
1454                         after the data has been parsed.
1455 */
1456
1457 /*!
1458 Returns a QQmlTypeData for the specified \a url.  The QQmlTypeData may be cached.
1459 */
1460 QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode)
1461 {
1462     Q_ASSERT(!url.isRelative() && 
1463             (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
1464              !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
1465
1466     LockHolder<QQmlTypeLoader> holder(this);
1467     
1468     QQmlTypeData *typeData = m_typeCache.value(url);
1469
1470     if (!typeData) {
1471         typeData = new QQmlTypeData(url, None, this);
1472         // TODO: if (compiledData == 0), is it safe to omit this insertion?
1473         m_typeCache.insert(url, typeData);
1474         QQmlDataLoader::load(typeData, mode);
1475     }
1476
1477     typeData->addref();
1478
1479     return typeData;
1480 }
1481
1482 /*!
1483 Returns a QQmlTypeData for the given \a data with the provided base \a url.  The 
1484 QQmlTypeData will not be cached.
1485
1486 The specified \a options control how the loader handles type data.
1487 */
1488 QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Options options)
1489 {
1490     LockHolder<QQmlTypeLoader> holder(this);
1491
1492     QQmlTypeData *typeData = new QQmlTypeData(url, options, this);
1493     QQmlDataLoader::loadWithStaticData(typeData, data);
1494
1495     return typeData;
1496 }
1497
1498 /*!
1499 Return a QQmlScriptBlob for \a url.  The QQmlScriptData may be cached.
1500 */
1501 QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url)
1502 {
1503     Q_ASSERT(!url.isRelative() && 
1504             (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
1505              !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
1506
1507     LockHolder<QQmlTypeLoader> holder(this);
1508
1509     QQmlScriptBlob *scriptBlob = m_scriptCache.value(url);
1510
1511     if (!scriptBlob) {
1512         scriptBlob = new QQmlScriptBlob(url, this);
1513         m_scriptCache.insert(url, scriptBlob);
1514         QQmlDataLoader::load(scriptBlob);
1515     }
1516
1517     scriptBlob->addref();
1518
1519     return scriptBlob;
1520 }
1521
1522 /*!
1523 Returns a QQmlQmldirData for \a url.  The QQmlQmldirData may be cached.
1524 */
1525 QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
1526 {
1527     Q_ASSERT(!url.isRelative() && 
1528             (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
1529              !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
1530
1531     LockHolder<QQmlTypeLoader> holder(this);
1532
1533     QQmlQmldirData *qmldirData = m_qmldirCache.value(url);
1534
1535     if (!qmldirData) {
1536         qmldirData = new QQmlQmldirData(url, this);
1537         m_qmldirCache.insert(url, qmldirData);
1538         QQmlDataLoader::load(qmldirData);
1539     }
1540
1541     qmldirData->addref();
1542
1543     return qmldirData;
1544 }
1545
1546 /*!
1547 Returns a QQmlBundleData for \a identifier.
1548 */
1549 QQmlBundleData *QQmlTypeLoader::getBundle(const QString &identifier)
1550 {
1551     return getBundle(QHashedStringRef(identifier));
1552 }
1553
1554 QQmlBundleData *QQmlTypeLoader::getBundle(const QHashedStringRef &identifier)
1555 {
1556     lock();
1557
1558     QQmlBundleData *rv = 0;
1559     QQmlBundleData **bundle = m_bundleCache.value(identifier);
1560     if (bundle) {
1561         rv = *bundle;
1562         rv->addref();
1563     }
1564
1565     unlock();
1566
1567     return rv;
1568 }
1569
1570 QQmlBundleData::QQmlBundleData(const QString &file)
1571 : QQmlBundle(file), fileName(file)
1572 {
1573 }
1574
1575 // XXX check for errors etc.
1576 void QQmlTypeLoader::addBundle(const QString &identifier, const QString &fileName)
1577 {
1578     lock();
1579     addBundleNoLock(identifier, fileName);
1580     unlock();
1581 }
1582
1583 void QQmlTypeLoader::addBundleNoLock(const QString &identifier, const QString &fileName)
1584 {
1585     QQmlBundleData *data = new QQmlBundleData(fileName);
1586     if (data->open()) {
1587
1588         m_bundleCache.insert(identifier, data);
1589
1590     } else {
1591         data->release();
1592     }
1593 }
1594
1595 QString QQmlTypeLoader::bundleIdForQmldir(const QString &name, const QString &uriHint)
1596 {
1597     lock();
1598     QString *bundleId = m_qmldirBundleIdCache.value(name);
1599     if (!bundleId) {
1600         QString newBundleId = QLatin1String("qml.") + uriHint.toLower() /* XXX toLower()? */;
1601         if (m_qmldirBundleIdCache.contains(newBundleId))
1602             newBundleId += QString::number(m_qmldirBundleIdCache.count());
1603         m_qmldirBundleIdCache.insert(name, newBundleId);
1604         addBundleNoLock(newBundleId, name);
1605         unlock();
1606         return newBundleId;
1607     } else {
1608         unlock();
1609         return *bundleId;
1610     }
1611 }
1612
1613 bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName)
1614 {
1615     Q_D(QQmlEngine);
1616
1617     if (name.startsWith(QLatin1String("qml."))) // reserved
1618         return false;
1619
1620     d->typeLoader.addBundle(name, fileName);
1621     return true;
1622 }
1623
1624 /*!
1625 Returns the absolute filename of path via a directory cache.
1626 Returns a empty string if the path does not exist.
1627
1628 Why a directory cache?  QML checks for files in many paths with
1629 invalid directories.  By caching whether a directory exists
1630 we avoid many stats.  We also cache the files' existance in the
1631 directory, for the same reason.
1632 */
1633 QString QQmlTypeLoader::absoluteFilePath(const QString &path)
1634 {
1635     if (path.isEmpty())
1636         return QString();
1637     if (path.at(0) == QLatin1Char(':')) {
1638         // qrc resource
1639         QFileInfo fileInfo(path);
1640         return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1641     } else if (path.count() > 3 && path.at(3) == QLatin1Char(':') &&
1642                path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) {
1643         // qrc resource url
1644         QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path));
1645         return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1646     }
1647     int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1648     QStringRef dirPath(&path, 0, lastSlash);
1649
1650     StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1651     if (!fileSet) {
1652         QHashedString dirPathString(dirPath.toString());
1653         bool exists = false;
1654 #ifdef Q_OS_UNIX
1655         struct stat statBuf;
1656         if (::stat(QFile::encodeName(dirPathString).constData(), &statBuf) == 0)
1657             exists = S_ISDIR(statBuf.st_mode);
1658 #else
1659         exists = QDir(dirPathString).exists();
1660 #endif
1661         QStringHash<bool> *files = exists ? new QStringHash<bool> : 0;
1662         m_importDirCache.insert(dirPathString, files);
1663         fileSet = m_importDirCache.value(dirPathString);
1664     }
1665     if (!(*fileSet))
1666         return QString();
1667
1668     QString absoluteFilePath;
1669     QHashedStringRef fileName(path.constData()+lastSlash+1, path.length()-lastSlash-1);
1670
1671     bool *value = (*fileSet)->value(fileName);
1672     if (value) {
1673         if (*value)
1674             absoluteFilePath = path;
1675     } else {
1676         bool exists = false;
1677 #ifdef Q_OS_UNIX
1678         struct stat statBuf;
1679         // XXX Avoid encoding entire path. Should store encoded dirpath in cache
1680         if (::stat(QFile::encodeName(path).constData(), &statBuf) == 0)
1681             exists = S_ISREG(statBuf.st_mode);
1682 #else
1683         exists = QFile::exists(path);
1684 #endif
1685         (*fileSet)->insert(fileName.toString(), exists);
1686         if (exists)
1687             absoluteFilePath = path;
1688     }
1689
1690     if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':'))
1691         absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath();
1692
1693     return absoluteFilePath;
1694 }
1695
1696
1697 /*!
1698 Returns true if the path is a directory via a directory cache.  Cache is
1699 shared with absoluteFilePath().
1700 */
1701 bool QQmlTypeLoader::directoryExists(const QString &path)
1702 {
1703     if (path.isEmpty())
1704         return false;
1705     if (path.at(0) == QLatin1Char(':')) {
1706         // qrc resource
1707         QFileInfo fileInfo(path);
1708         return fileInfo.exists() && fileInfo.isDir();
1709     }
1710
1711     int length = path.length();
1712     if (path.endsWith(QLatin1Char('/')))
1713         --length;
1714     QStringRef dirPath(&path, 0, length);
1715
1716     StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1717     if (!fileSet) {
1718         QHashedString dirPathString(dirPath.toString());
1719         bool exists = false;
1720 #ifdef Q_OS_UNIX
1721         struct stat statBuf;
1722         if (::stat(QFile::encodeName(dirPathString).constData(), &statBuf) == 0)
1723             exists = S_ISDIR(statBuf.st_mode);
1724 #else
1725         exists = QDir(dirPathString).exists();
1726 #endif
1727         QStringHash<bool> *files = exists ? new QStringHash<bool> : 0;
1728         m_importDirCache.insert(dirPathString, files);
1729         fileSet = m_importDirCache.value(dirPathString);
1730     }
1731
1732     return (*fileSet);
1733 }
1734
1735
1736 /*!
1737 Return a QmldirContent for absoluteFilePath.  The QmldirContent may be cached.
1738
1739 \a filePath is either a bundle URL, or a local file path.
1740 */
1741 const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePath, const QString &uriHint)
1742 {
1743     QmldirContent *qmldir;
1744     QmldirContent **val = m_importQmlDirCache.value(filePath);
1745     if (!val) {
1746         qmldir = new QmldirContent;
1747
1748 #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); }
1749 #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable"))
1750 #define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\""))
1751
1752         if (QQmlFile::isBundle(filePath)) {
1753
1754             QUrl url(filePath);
1755
1756             QQmlFile file(engine(), url);
1757             if (file.isError()) {
1758                 ERROR(NOT_READABLE_ERROR.arg(filePath));
1759             } else {
1760                 QString content(QString::fromUtf8(file.data(), file.size()));
1761                 qmldir->setContent(filePath, content);
1762             }
1763
1764         } else {
1765
1766             QFile file(filePath);
1767             if (!QQml_isFileCaseCorrect(filePath)) {
1768                 ERROR(CASE_MISMATCH_ERROR.arg(filePath));
1769             } else if (file.open(QFile::ReadOnly)) {
1770                 QByteArray data = file.read(QQmlBundle::bundleHeaderLength());
1771
1772                 if (QQmlBundle::isBundleHeader(data.constData(), data.length())) {
1773                     QString id = bundleIdForQmldir(filePath, uriHint);
1774
1775                     QString bundleUrl = QLatin1String("bundle://") + id + QLatin1Char('/');
1776
1777                     QUrl url(bundleUrl + QLatin1String("qmldir"));
1778
1779                     QQmlFile file(engine(), url);
1780                     if (file.isError()) {
1781                         ERROR(NOT_READABLE_ERROR.arg(filePath));
1782                     } else {
1783                         QString content(QString::fromUtf8(file.data(), file.size()));
1784                         qmldir->setContent(QQmlFile::bundleFileName(bundleUrl, engine()), content);
1785                     }
1786                 } else {
1787                     data += file.readAll();
1788                     qmldir->setContent(filePath, QString::fromUtf8(data));
1789                 }
1790             } else {
1791                 ERROR(NOT_READABLE_ERROR.arg(filePath));
1792             }
1793
1794         }
1795
1796 #undef ERROR
1797 #undef NOT_READABLE_ERROR
1798 #undef CASE_MISMATCH_ERROR
1799
1800         m_importQmlDirCache.insert(filePath, qmldir);
1801     } else {
1802         qmldir = *val;
1803     }
1804
1805     return qmldir;
1806 }
1807
1808 void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content)
1809 {
1810     QmldirContent *qmldir;
1811     QmldirContent **val = m_importQmlDirCache.value(url);
1812     if (val) {
1813         qmldir = *val;
1814     } else {
1815         qmldir = new QmldirContent;
1816         m_importQmlDirCache.insert(url, qmldir);
1817     }
1818
1819     qmldir->setContent(url, content);
1820 }
1821
1822 /*!
1823 Clears cached information about loaded files, including any type data, scripts
1824 and qmldir information.
1825 */
1826 void QQmlTypeLoader::clearCache()
1827 {
1828     for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter)
1829         (*iter)->release();
1830     for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) 
1831         (*iter)->release();
1832     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter)
1833         (*iter)->release();
1834     qDeleteAll(m_importDirCache);
1835     qDeleteAll(m_importQmlDirCache);
1836
1837     m_typeCache.clear();
1838     m_scriptCache.clear();
1839     m_qmldirCache.clear();
1840     m_importDirCache.clear();
1841     m_importQmlDirCache.clear();
1842 }
1843
1844 void QQmlTypeLoader::trimCache()
1845 {
1846     while (true) {
1847         QList<TypeCache::Iterator> unneededTypes;
1848         for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter)  {
1849             QQmlTypeData *typeData = iter.value();
1850             if (typeData->m_compiledData && typeData->m_compiledData->count() == 1) {
1851                 // There are no live objects of this type
1852                 unneededTypes.append(iter);
1853             }
1854         }
1855
1856         if (unneededTypes.isEmpty())
1857             break;
1858
1859         while (!unneededTypes.isEmpty()) {
1860             TypeCache::Iterator iter = unneededTypes.last();
1861             unneededTypes.removeLast();
1862
1863             iter.value()->release();
1864             m_typeCache.erase(iter);
1865         }
1866     }
1867
1868     // TODO: release any scripts which are no longer referenced by any types
1869 }
1870
1871 bool QQmlTypeLoader::isTypeLoaded(const QUrl &url) const
1872 {
1873     LockHolder<QQmlTypeLoader> holder(const_cast<QQmlTypeLoader *>(this));
1874     return m_typeCache.contains(url);
1875 }
1876
1877 bool QQmlTypeLoader::isScriptLoaded(const QUrl &url) const
1878 {
1879     LockHolder<QQmlTypeLoader> holder(const_cast<QQmlTypeLoader *>(this));
1880     return m_scriptCache.contains(url);
1881 }
1882
1883
1884 QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options, 
1885                                            QQmlTypeLoader *manager)
1886 : QQmlTypeLoader::Blob(url, QmlFile, manager), m_options(options),
1887    m_typesResolved(false), m_compiledData(0), m_implicitImport(0)
1888 {
1889 }
1890
1891 QQmlTypeData::~QQmlTypeData()
1892 {
1893     for (int ii = 0; ii < m_scripts.count(); ++ii) 
1894         m_scripts.at(ii).script->release();
1895     for (int ii = 0; ii < m_types.count(); ++ii) 
1896         if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1897     if (m_compiledData)
1898         m_compiledData->release();
1899     delete m_implicitImport;
1900 }
1901
1902 const QQmlScript::Parser &QQmlTypeData::parser() const
1903 {
1904     return scriptParser;
1905 }
1906
1907 const QList<QQmlTypeData::TypeReference> &QQmlTypeData::resolvedTypes() const
1908 {
1909     return m_types;
1910 }
1911
1912 const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
1913 {
1914     return m_scripts;
1915 }
1916
1917 const QSet<QString> &QQmlTypeData::namespaces() const
1918 {
1919     return m_namespaces;
1920 }
1921
1922 QQmlCompiledData *QQmlTypeData::compiledData() const
1923 {
1924     return m_compiledData;
1925 }
1926
1927 void QQmlTypeData::registerCallback(TypeDataCallback *callback)
1928 {
1929     Q_ASSERT(!m_callbacks.contains(callback));
1930     m_callbacks.append(callback);
1931 }
1932
1933 void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
1934 {
1935     Q_ASSERT(m_callbacks.contains(callback));
1936     m_callbacks.removeOne(callback);
1937     Q_ASSERT(!m_callbacks.contains(callback));
1938 }
1939
1940 void QQmlTypeData::done()
1941 {
1942     // Check all script dependencies for errors
1943     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1944         const ScriptReference &script = m_scripts.at(ii);
1945         Q_ASSERT(script.script->isCompleteOrError());
1946         if (script.script->isError()) {
1947             QList<QQmlError> errors = script.script->errors();
1948             QQmlError error;
1949             error.setUrl(finalUrl());
1950             error.setLine(script.location.line);
1951             error.setColumn(script.location.column);
1952             error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1953             errors.prepend(error);
1954             setError(errors);
1955         }
1956     }
1957
1958     // Check all type dependencies for errors
1959     for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1960         const TypeReference &type = m_types.at(ii);
1961         Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1962         if (type.typeData && type.typeData->isError()) {
1963             QString typeName = scriptParser.referencedTypes().at(ii)->name;
1964
1965             QList<QQmlError> errors = type.typeData->errors();
1966             QQmlError error;
1967             error.setUrl(finalUrl());
1968             error.setLine(type.location.line);
1969             error.setColumn(type.location.column);
1970             error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
1971             errors.prepend(error);
1972             setError(errors);
1973         }
1974     }
1975
1976     // Compile component
1977     if (!isError()) 
1978         compile();
1979
1980     if (!(m_options & QQmlTypeLoader::PreserveParser))
1981         scriptParser.clear();
1982 }
1983
1984 void QQmlTypeData::completed()
1985 {
1986     // Notify callbacks
1987     while (!m_callbacks.isEmpty()) {
1988         TypeDataCallback *callback = m_callbacks.takeFirst();
1989         callback->typeDataReady(this);
1990     }
1991 }
1992
1993 void QQmlTypeData::dataReceived(const Data &data)
1994 {
1995     QString code = QString::fromUtf8(data.data(), data.size());
1996     QByteArray preparseData;
1997
1998     if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
1999
2000     if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
2001         setError(scriptParser.errors());
2002         return;
2003     }
2004
2005     m_imports.setBaseUrl(finalUrl(), finalUrlString());
2006
2007     QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
2008
2009     // For local urls, add an implicit import "." as first (most overridden) lookup.
2010     // This will also trigger the loading of the qmldir and the import of any native
2011     // types from available plugins.
2012     QList<QQmlError> implicitImportErrors;
2013     m_imports.addImplicitImport(importDatabase, &implicitImportErrors);
2014
2015     if (!implicitImportErrors.isEmpty()) {
2016         setError(implicitImportErrors);
2017         return;
2018     }
2019
2020     QList<QQmlError> errors;
2021
2022     if (!finalUrl().scheme().isEmpty()) {
2023         QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
2024         if (!QQmlImports::isLocal(qmldirUrl)) {
2025             // This qmldir is for the implicit import
2026             m_implicitImport = new QQmlScript::Import;
2027             m_implicitImport->uri = QLatin1String(".");
2028             m_implicitImport->qualifier = QString();
2029             m_implicitImport->majorVersion = -1;
2030             m_implicitImport->minorVersion = -1;
2031
2032             if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
2033                 setError(errors);
2034                 return;
2035             }
2036         }
2037     }
2038
2039     foreach (const QQmlScript::Import &import, scriptParser.imports()) {
2040         if (!addImport(import, &errors)) {
2041             Q_ASSERT(errors.size());
2042             QQmlError error(errors.takeFirst());
2043             error.setUrl(m_imports.baseUrl());
2044             error.setLine(import.location.start.line);
2045             error.setColumn(import.location.start.column);
2046             errors.prepend(error); // put it back on the list after filling out information.
2047             setError(errors);
2048             return;
2049         }
2050     }
2051 }
2052
2053 void QQmlTypeData::allDependenciesDone()
2054 {
2055     if (!m_typesResolved) {
2056         // Check that all imports were resolved
2057         QList<QQmlError> errors;
2058         QHash<const QQmlScript::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
2059         for ( ; it != end; ++it) {
2060             if (*it == 0) {
2061                 // This import was not resolved
2062                 foreach (const QQmlScript::Import *import, m_unresolvedImports.keys()) {
2063                     QQmlError error;
2064                     error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
2065                     error.setUrl(m_imports.baseUrl());
2066                     error.setLine(import->location.start.line);
2067                     error.setColumn(import->location.start.column);
2068                     errors.prepend(error);
2069                 }
2070             }
2071         }
2072         if (errors.size()) {
2073             setError(errors);
2074             return;
2075         }
2076
2077         resolveTypes();
2078         m_typesResolved = true;
2079     }
2080 }
2081
2082 void QQmlTypeData::downloadProgressChanged(qreal p)
2083 {
2084     for (int ii = 0; ii < m_callbacks.count(); ++ii) {
2085         TypeDataCallback *callback = m_callbacks.at(ii);
2086         callback->typeDataProgress(this, p);
2087     }
2088 }
2089
2090 void QQmlTypeData::compile()
2091 {
2092     Q_ASSERT(m_compiledData == 0);
2093
2094     m_compiledData = new QQmlCompiledData(typeLoader()->engine());
2095     m_compiledData->url = finalUrl();
2096     m_compiledData->name = finalUrlString();
2097
2098     QQmlCompilingProfiler prof(m_compiledData->name);
2099
2100     QQmlCompiler compiler(&scriptParser._pool);
2101     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
2102         setError(compiler.errors());
2103         m_compiledData->release();
2104         m_compiledData = 0;
2105     }
2106 }
2107
2108 void QQmlTypeData::resolveTypes()
2109 {
2110     // Add any imported scripts to our resolved set
2111     foreach (const QQmlImports::ScriptReference &script, m_imports.resolvedScripts())
2112     {
2113         QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
2114         addDependency(blob);
2115
2116         ScriptReference ref;
2117         //ref.location = ...
2118         ref.qualifier = script.nameSpace;
2119         if (!script.qualifier.isEmpty())
2120         {
2121             ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
2122
2123             // Add a reference to the enclosing namespace
2124             m_namespaces.insert(script.qualifier);
2125         }
2126
2127         ref.script = blob;
2128         m_scripts << ref;
2129     }
2130
2131     foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
2132         TypeReference ref;
2133
2134         QString url;
2135         int majorVersion;
2136         int minorVersion;
2137         QQmlImportNamespace *typeNamespace = 0;
2138         QList<QQmlError> errors;
2139
2140         if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
2141                                    &typeNamespace, &errors) || typeNamespace) {
2142             // Known to not be a type:
2143             //  - known to be a namespace (Namespace {})
2144             //  - type with unknown namespace (UnknownNamespace.SomeType {})
2145             QQmlError error;
2146             if (typeNamespace) {
2147                 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(parserRef->name));
2148             } else {
2149                 if (errors.size()) {
2150                     error = errors.takeFirst();
2151                 } else {
2152                     // this should not be possible!
2153                     // Description should come from error provided by addImport() function.
2154                     error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
2155                 }
2156                 error.setUrl(m_imports.baseUrl());
2157                 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(parserRef->name).arg(error.description()));
2158             }
2159
2160             Q_ASSERT(parserRef->firstUse);
2161             error.setLine(parserRef->firstUse->location.start.line);
2162             error.setColumn(parserRef->firstUse->location.start.column);
2163
2164             errors.prepend(error);
2165             setError(errors);
2166             return;
2167         }
2168
2169         if (ref.type) {
2170             ref.majorVersion = majorVersion;
2171             ref.minorVersion = minorVersion;
2172         } else {
2173             ref.typeData = typeLoader()->getType(QUrl(url));
2174             addDependency(ref.typeData);
2175         }
2176
2177         Q_ASSERT(parserRef->firstUse);
2178         ref.location = parserRef->firstUse->location.start;
2179
2180         m_types << ref;
2181     }
2182 }
2183
2184 void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
2185 {
2186     ScriptReference ref;
2187     ref.script = blob;
2188     ref.location = location;
2189     ref.qualifier = qualifier;
2190
2191     m_scripts << ref;
2192 }
2193
2194 QQmlScriptData::QQmlScriptData()
2195 : importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false) 
2196 {
2197 }
2198
2199 QQmlScriptData::~QQmlScriptData()
2200 {
2201 }
2202
2203 void QQmlScriptData::clear()
2204 {
2205     if (importCache) {
2206         importCache->release();
2207         importCache = 0;
2208     }
2209
2210     for (int ii = 0; ii < scripts.count(); ++ii)
2211         scripts.at(ii)->release();
2212     scripts.clear();
2213
2214     qPersistentDispose(m_program);
2215     qPersistentDispose(m_value);
2216
2217     // An addref() was made when the QQmlCleanup was added to the engine.
2218     release();
2219 }
2220
2221 QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
2222 : QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(0)
2223 {
2224 }
2225
2226 QQmlScriptBlob::~QQmlScriptBlob()
2227 {
2228     if (m_scriptData) {
2229         m_scriptData->release();
2230         m_scriptData = 0;
2231     }
2232 }
2233
2234 QQmlScript::Object::ScriptBlock::Pragmas QQmlScriptBlob::pragmas() const
2235 {
2236     return m_metadata.pragmas;
2237 }
2238
2239 QQmlScriptData *QQmlScriptBlob::scriptData() const
2240 {
2241     return m_scriptData;
2242 }
2243
2244 void QQmlScriptBlob::dataReceived(const Data &data)
2245 {
2246     m_source = QString::fromUtf8(data.data(), data.size());
2247
2248     m_scriptData = new QQmlScriptData();
2249     m_scriptData->url = finalUrl();
2250     m_scriptData->urlString = finalUrlString();
2251
2252     QQmlError metaDataError;
2253     m_metadata = QQmlScript::Parser::extractMetaData(m_source, &metaDataError);
2254     if (metaDataError.isValid()) {
2255         metaDataError.setUrl(finalUrl());
2256         m_scriptData->setError(metaDataError);
2257     }
2258
2259     m_imports.setBaseUrl(finalUrl(), finalUrlString());
2260
2261     QList<QQmlError> errors;
2262
2263     foreach (const QQmlScript::Import &import, m_metadata.imports) {
2264         if (!addImport(import, &errors)) {
2265             Q_ASSERT(errors.size());
2266             QQmlError error(errors.takeFirst());
2267             error.setUrl(m_imports.baseUrl());
2268             error.setLine(import.location.start.line);
2269             error.setColumn(import.location.start.column);
2270             errors.prepend(error); // put it back on the list after filling out information.
2271             setError(errors);
2272             return;
2273         }
2274     }
2275 }
2276
2277 void QQmlScriptBlob::done()
2278 {
2279     // Check all script dependencies for errors
2280     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
2281         const ScriptReference &script = m_scripts.at(ii);
2282         Q_ASSERT(script.script->isCompleteOrError());
2283         if (script.script->isError()) {
2284             QList<QQmlError> errors = script.script->errors();
2285             QQmlError error;
2286             error.setUrl(finalUrl());
2287             error.setLine(script.location.line);
2288             error.setColumn(script.location.column);
2289             error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
2290             errors.prepend(error);
2291             setError(errors);
2292         }
2293     }
2294
2295     if (isError())
2296         return;
2297
2298     QQmlEngine *engine = typeLoader()->engine();
2299
2300     m_scriptData->importCache = new QQmlTypeNameCache();
2301
2302     QSet<QString> ns;
2303
2304     for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) {
2305         const ScriptReference &script = m_scripts.at(scriptIndex);
2306
2307         m_scriptData->scripts.append(script.script);
2308
2309         if (!script.nameSpace.isNull()) {
2310             if (!ns.contains(script.nameSpace)) {
2311                 ns.insert(script.nameSpace);
2312                 m_scriptData->importCache->add(script.nameSpace);
2313             }
2314         }
2315         m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace);
2316     }
2317
2318     m_imports.populateCache(m_scriptData->importCache, engine);
2319
2320     m_scriptData->pragmas = m_metadata.pragmas;
2321     m_scriptData->m_programSource = m_source.toUtf8();
2322     m_source.clear();
2323 }
2324
2325 void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace)
2326 {
2327     ScriptReference ref;
2328     ref.script = blob;
2329     ref.location = location;
2330     ref.qualifier = qualifier;
2331     ref.nameSpace = nameSpace;
2332
2333     m_scripts << ref;
2334 }
2335
2336 QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
2337 : QQmlTypeLoader::Blob(url, QmldirFile, loader), m_import(0), m_priority(0)
2338 {
2339 }
2340
2341 const QString &QQmlQmldirData::content() const
2342 {
2343     return m_content;
2344 }
2345
2346 const QQmlScript::Import *QQmlQmldirData::import() const
2347 {
2348     return m_import;
2349 }
2350
2351 void QQmlQmldirData::setImport(const QQmlScript::Import *import)
2352 {
2353     m_import = import;
2354 }
2355
2356 int QQmlQmldirData::priority() const
2357 {
2358     return m_priority;
2359 }
2360
2361 void QQmlQmldirData::setPriority(int priority)
2362 {
2363     m_priority = priority;
2364 }
2365
2366 void QQmlQmldirData::dataReceived(const Data &data)
2367 {
2368     m_content = QString::fromUtf8(data.data(), data.size());
2369 }
2370
2371 QT_END_NAMESPACE
2372
2373 #include "qqmltypeloader.moc"