Improve documentation.
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmltypeloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml 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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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' existence 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 QQmlTypeData::TypeDataCallback::~TypeDataCallback()
1884 {
1885 }
1886
1887 QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options, 
1888                                            QQmlTypeLoader *manager)
1889 : QQmlTypeLoader::Blob(url, QmlFile, manager), m_options(options),
1890    m_typesResolved(false), m_compiledData(0), m_implicitImport(0)
1891 {
1892 }
1893
1894 QQmlTypeData::~QQmlTypeData()
1895 {
1896     for (int ii = 0; ii < m_scripts.count(); ++ii) 
1897         m_scripts.at(ii).script->release();
1898     for (int ii = 0; ii < m_types.count(); ++ii) 
1899         if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1900     if (m_compiledData)
1901         m_compiledData->release();
1902     delete m_implicitImport;
1903 }
1904
1905 const QQmlScript::Parser &QQmlTypeData::parser() const
1906 {
1907     return scriptParser;
1908 }
1909
1910 const QList<QQmlTypeData::TypeReference> &QQmlTypeData::resolvedTypes() const
1911 {
1912     return m_types;
1913 }
1914
1915 const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
1916 {
1917     return m_scripts;
1918 }
1919
1920 const QSet<QString> &QQmlTypeData::namespaces() const
1921 {
1922     return m_namespaces;
1923 }
1924
1925 QQmlCompiledData *QQmlTypeData::compiledData() const
1926 {
1927     return m_compiledData;
1928 }
1929
1930 void QQmlTypeData::registerCallback(TypeDataCallback *callback)
1931 {
1932     Q_ASSERT(!m_callbacks.contains(callback));
1933     m_callbacks.append(callback);
1934 }
1935
1936 void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
1937 {
1938     Q_ASSERT(m_callbacks.contains(callback));
1939     m_callbacks.removeOne(callback);
1940     Q_ASSERT(!m_callbacks.contains(callback));
1941 }
1942
1943 void QQmlTypeData::done()
1944 {
1945     // Check all script dependencies for errors
1946     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1947         const ScriptReference &script = m_scripts.at(ii);
1948         Q_ASSERT(script.script->isCompleteOrError());
1949         if (script.script->isError()) {
1950             QList<QQmlError> errors = script.script->errors();
1951             QQmlError error;
1952             error.setUrl(finalUrl());
1953             error.setLine(script.location.line);
1954             error.setColumn(script.location.column);
1955             error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1956             errors.prepend(error);
1957             setError(errors);
1958         }
1959     }
1960
1961     // Check all type dependencies for errors
1962     for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1963         const TypeReference &type = m_types.at(ii);
1964         Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1965         if (type.typeData && type.typeData->isError()) {
1966             QString typeName = scriptParser.referencedTypes().at(ii)->name;
1967
1968             QList<QQmlError> errors = type.typeData->errors();
1969             QQmlError error;
1970             error.setUrl(finalUrl());
1971             error.setLine(type.location.line);
1972             error.setColumn(type.location.column);
1973             error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
1974             errors.prepend(error);
1975             setError(errors);
1976         }
1977     }
1978
1979     // Compile component
1980     if (!isError()) 
1981         compile();
1982
1983     if (!(m_options & QQmlTypeLoader::PreserveParser))
1984         scriptParser.clear();
1985 }
1986
1987 void QQmlTypeData::completed()
1988 {
1989     // Notify callbacks
1990     while (!m_callbacks.isEmpty()) {
1991         TypeDataCallback *callback = m_callbacks.takeFirst();
1992         callback->typeDataReady(this);
1993     }
1994 }
1995
1996 void QQmlTypeData::dataReceived(const Data &data)
1997 {
1998     QString code = QString::fromUtf8(data.data(), data.size());
1999     QByteArray preparseData;
2000
2001     if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
2002
2003     if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
2004         setError(scriptParser.errors());
2005         return;
2006     }
2007
2008     m_imports.setBaseUrl(finalUrl(), finalUrlString());
2009
2010     QQmlImportDatabase *importDatabase = typeLoader()->importDatabase();
2011
2012     // For local urls, add an implicit import "." as first (most overridden) lookup.
2013     // This will also trigger the loading of the qmldir and the import of any native
2014     // types from available plugins.
2015     QList<QQmlError> implicitImportErrors;
2016     m_imports.addImplicitImport(importDatabase, &implicitImportErrors);
2017
2018     if (!implicitImportErrors.isEmpty()) {
2019         setError(implicitImportErrors);
2020         return;
2021     }
2022
2023     QList<QQmlError> errors;
2024
2025     if (!finalUrl().scheme().isEmpty()) {
2026         QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
2027         if (!QQmlImports::isLocal(qmldirUrl)) {
2028             // This qmldir is for the implicit import
2029             m_implicitImport = new QQmlScript::Import;
2030             m_implicitImport->uri = QLatin1String(".");
2031             m_implicitImport->qualifier = QString();
2032             m_implicitImport->majorVersion = -1;
2033             m_implicitImport->minorVersion = -1;
2034
2035             if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
2036                 setError(errors);
2037                 return;
2038             }
2039         }
2040     }
2041
2042     foreach (const QQmlScript::Import &import, scriptParser.imports()) {
2043         if (!addImport(import, &errors)) {
2044             Q_ASSERT(errors.size());
2045             QQmlError error(errors.takeFirst());
2046             error.setUrl(m_imports.baseUrl());
2047             error.setLine(import.location.start.line);
2048             error.setColumn(import.location.start.column);
2049             errors.prepend(error); // put it back on the list after filling out information.
2050             setError(errors);
2051             return;
2052         }
2053     }
2054 }
2055
2056 void QQmlTypeData::allDependenciesDone()
2057 {
2058     if (!m_typesResolved) {
2059         // Check that all imports were resolved
2060         QList<QQmlError> errors;
2061         QHash<const QQmlScript::Import *, int>::const_iterator it = m_unresolvedImports.constBegin(), end = m_unresolvedImports.constEnd();
2062         for ( ; it != end; ++it) {
2063             if (*it == 0) {
2064                 // This import was not resolved
2065                 foreach (const QQmlScript::Import *import, m_unresolvedImports.keys()) {
2066                     QQmlError error;
2067                     error.setDescription(QQmlTypeLoader::tr("module \"%1\" is not installed").arg(import->uri));
2068                     error.setUrl(m_imports.baseUrl());
2069                     error.setLine(import->location.start.line);
2070                     error.setColumn(import->location.start.column);
2071                     errors.prepend(error);
2072                 }
2073             }
2074         }
2075         if (errors.size()) {
2076             setError(errors);
2077             return;
2078         }
2079
2080         resolveTypes();
2081         m_typesResolved = true;
2082     }
2083 }
2084
2085 void QQmlTypeData::downloadProgressChanged(qreal p)
2086 {
2087     for (int ii = 0; ii < m_callbacks.count(); ++ii) {
2088         TypeDataCallback *callback = m_callbacks.at(ii);
2089         callback->typeDataProgress(this, p);
2090     }
2091 }
2092
2093 void QQmlTypeData::compile()
2094 {
2095     Q_ASSERT(m_compiledData == 0);
2096
2097     m_compiledData = new QQmlCompiledData(typeLoader()->engine());
2098     m_compiledData->url = finalUrl();
2099     m_compiledData->name = finalUrlString();
2100
2101     QQmlCompilingProfiler prof(m_compiledData->name);
2102
2103     QQmlCompiler compiler(&scriptParser._pool);
2104     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
2105         setError(compiler.errors());
2106         m_compiledData->release();
2107         m_compiledData = 0;
2108     }
2109 }
2110
2111 void QQmlTypeData::resolveTypes()
2112 {
2113     // Add any imported scripts to our resolved set
2114     foreach (const QQmlImports::ScriptReference &script, m_imports.resolvedScripts())
2115     {
2116         QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
2117         addDependency(blob);
2118
2119         ScriptReference ref;
2120         //ref.location = ...
2121         ref.qualifier = script.nameSpace;
2122         if (!script.qualifier.isEmpty())
2123         {
2124             ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
2125
2126             // Add a reference to the enclosing namespace
2127             m_namespaces.insert(script.qualifier);
2128         }
2129
2130         ref.script = blob;
2131         m_scripts << ref;
2132     }
2133
2134     foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
2135         TypeReference ref;
2136
2137         QString url;
2138         int majorVersion;
2139         int minorVersion;
2140         QQmlImportNamespace *typeNamespace = 0;
2141         QList<QQmlError> errors;
2142
2143         if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
2144                                    &typeNamespace, &errors) || typeNamespace) {
2145             // Known to not be a type:
2146             //  - known to be a namespace (Namespace {})
2147             //  - type with unknown namespace (UnknownNamespace.SomeType {})
2148             QQmlError error;
2149             if (typeNamespace) {
2150                 error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(parserRef->name));
2151             } else {
2152                 if (errors.size()) {
2153                     error = errors.takeFirst();
2154                 } else {
2155                     // this should not be possible!
2156                     // Description should come from error provided by addImport() function.
2157                     error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database"));
2158                 }
2159                 error.setUrl(m_imports.baseUrl());
2160                 error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(parserRef->name).arg(error.description()));
2161             }
2162
2163             Q_ASSERT(parserRef->firstUse);
2164             error.setLine(parserRef->firstUse->location.start.line);
2165             error.setColumn(parserRef->firstUse->location.start.column);
2166
2167             errors.prepend(error);
2168             setError(errors);
2169             return;
2170         }
2171
2172         if (ref.type) {
2173             ref.majorVersion = majorVersion;
2174             ref.minorVersion = minorVersion;
2175         } else {
2176             ref.typeData = typeLoader()->getType(QUrl(url));
2177             addDependency(ref.typeData);
2178         }
2179
2180         Q_ASSERT(parserRef->firstUse);
2181         ref.location = parserRef->firstUse->location.start;
2182
2183         m_types << ref;
2184     }
2185 }
2186
2187 void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
2188 {
2189     ScriptReference ref;
2190     ref.script = blob;
2191     ref.location = location;
2192     ref.qualifier = qualifier;
2193
2194     m_scripts << ref;
2195 }
2196
2197 QQmlScriptData::QQmlScriptData()
2198 : importCache(0), pragmas(QQmlScript::Object::ScriptBlock::None), m_loaded(false) 
2199 {
2200 }
2201
2202 QQmlScriptData::~QQmlScriptData()
2203 {
2204 }
2205
2206 void QQmlScriptData::clear()
2207 {
2208     if (importCache) {
2209         importCache->release();
2210         importCache = 0;
2211     }
2212
2213     for (int ii = 0; ii < scripts.count(); ++ii)
2214         scripts.at(ii)->release();
2215     scripts.clear();
2216
2217     qPersistentDispose(m_program);
2218     qPersistentDispose(m_value);
2219
2220     // An addref() was made when the QQmlCleanup was added to the engine.
2221     release();
2222 }
2223
2224 QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
2225 : QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(0)
2226 {
2227 }
2228
2229 QQmlScriptBlob::~QQmlScriptBlob()
2230 {
2231     if (m_scriptData) {
2232         m_scriptData->release();
2233         m_scriptData = 0;
2234     }
2235 }
2236
2237 QQmlScript::Object::ScriptBlock::Pragmas QQmlScriptBlob::pragmas() const
2238 {
2239     return m_metadata.pragmas;
2240 }
2241
2242 QQmlScriptData *QQmlScriptBlob::scriptData() const
2243 {
2244     return m_scriptData;
2245 }
2246
2247 void QQmlScriptBlob::dataReceived(const Data &data)
2248 {
2249     m_source = QString::fromUtf8(data.data(), data.size());
2250
2251     m_scriptData = new QQmlScriptData();
2252     m_scriptData->url = finalUrl();
2253     m_scriptData->urlString = finalUrlString();
2254
2255     QQmlError metaDataError;
2256     m_metadata = QQmlScript::Parser::extractMetaData(m_source, &metaDataError);
2257     if (metaDataError.isValid()) {
2258         metaDataError.setUrl(finalUrl());
2259         m_scriptData->setError(metaDataError);
2260     }
2261
2262     m_imports.setBaseUrl(finalUrl(), finalUrlString());
2263
2264     QList<QQmlError> errors;
2265
2266     foreach (const QQmlScript::Import &import, m_metadata.imports) {
2267         if (!addImport(import, &errors)) {
2268             Q_ASSERT(errors.size());
2269             QQmlError error(errors.takeFirst());
2270             error.setUrl(m_imports.baseUrl());
2271             error.setLine(import.location.start.line);
2272             error.setColumn(import.location.start.column);
2273             errors.prepend(error); // put it back on the list after filling out information.
2274             setError(errors);
2275             return;
2276         }
2277     }
2278 }
2279
2280 void QQmlScriptBlob::done()
2281 {
2282     // Check all script dependencies for errors
2283     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
2284         const ScriptReference &script = m_scripts.at(ii);
2285         Q_ASSERT(script.script->isCompleteOrError());
2286         if (script.script->isError()) {
2287             QList<QQmlError> errors = script.script->errors();
2288             QQmlError error;
2289             error.setUrl(finalUrl());
2290             error.setLine(script.location.line);
2291             error.setColumn(script.location.column);
2292             error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
2293             errors.prepend(error);
2294             setError(errors);
2295         }
2296     }
2297
2298     if (isError())
2299         return;
2300
2301     m_scriptData->importCache = new QQmlTypeNameCache();
2302
2303     QSet<QString> ns;
2304
2305     for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) {
2306         const ScriptReference &script = m_scripts.at(scriptIndex);
2307
2308         m_scriptData->scripts.append(script.script);
2309
2310         if (!script.nameSpace.isNull()) {
2311             if (!ns.contains(script.nameSpace)) {
2312                 ns.insert(script.nameSpace);
2313                 m_scriptData->importCache->add(script.nameSpace);
2314             }
2315         }
2316         m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace);
2317     }
2318
2319     m_imports.populateCache(m_scriptData->importCache);
2320
2321     m_scriptData->pragmas = m_metadata.pragmas;
2322     m_scriptData->m_programSource = m_source.toUtf8();
2323     m_source.clear();
2324 }
2325
2326 void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace)
2327 {
2328     ScriptReference ref;
2329     ref.script = blob;
2330     ref.location = location;
2331     ref.qualifier = qualifier;
2332     ref.nameSpace = nameSpace;
2333
2334     m_scripts << ref;
2335 }
2336
2337 QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
2338 : QQmlTypeLoader::Blob(url, QmldirFile, loader), m_import(0), m_priority(0)
2339 {
2340 }
2341
2342 const QString &QQmlQmldirData::content() const
2343 {
2344     return m_content;
2345 }
2346
2347 const QQmlScript::Import *QQmlQmldirData::import() const
2348 {
2349     return m_import;
2350 }
2351
2352 void QQmlQmldirData::setImport(const QQmlScript::Import *import)
2353 {
2354     m_import = import;
2355 }
2356
2357 int QQmlQmldirData::priority() const
2358 {
2359     return m_priority;
2360 }
2361
2362 void QQmlQmldirData::setPriority(int priority)
2363 {
2364     m_priority = priority;
2365 }
2366
2367 void QQmlQmldirData::dataReceived(const Data &data)
2368 {
2369     m_content = QString::fromUtf8(data.data(), data.size());
2370 }
2371
2372 QT_END_NAMESPACE
2373
2374 #include "qqmltypeloader.moc"