bf319e53870b19a9b6cde6608d5551cf10197f74
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativetypeloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativetypeloader_p.h"
43
44 #include <private/qdeclarativeengine_p.h>
45 #include <private/qdeclarativeglobal_p.h>
46 #include <private/qdeclarativethread_p.h>
47 #include <private/qdeclarativecompiler_p.h>
48 #include <private/qdeclarativecomponent_p.h>
49 #include <private/qdeclarativedebugtrace_p.h>
50
51 #include <QtCore/qdir.h>
52 #include <QtCore/qfile.h>
53 #include <QtCore/qdebug.h>
54 #include <QtCore/qmutex.h>
55 #include <QtCore/qthread.h>
56 #include <QtCore/qdiriterator.h>
57 #include <QtCore/qwaitcondition.h>
58 #include <QtDeclarative/qdeclarativecomponent.h>
59 #include <QtDeclarative/qdeclarativeextensioninterface.h>
60
61 #if defined (Q_OS_UNIX)
62 #include <sys/types.h>
63 #include <dirent.h>
64 #endif
65
66 // #define DATABLOB_DEBUG
67
68 #ifdef DATABLOB_DEBUG
69
70 #define ASSERT_MAINTHREAD() do { if(m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in main thread"); } while(false)
71 #define ASSERT_LOADTHREAD() do { if(!m_thread->isThisThread()) qFatal("QDeclarativeDataLoader: Caller not in load thread"); } while(false)
72 #define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QDeclarativeDataBlob: An API call was made outside a callback"); } while(false)
73
74 #else
75
76 #define ASSERT_MAINTHREAD() 
77 #define ASSERT_LOADTHREAD()
78 #define ASSERT_CALLBACK()
79
80 #endif
81
82 QT_BEGIN_NAMESPACE
83
84 // This is a lame object that we need to ensure that slots connected to
85 // QNetworkReply get called in the correct thread (the loader thread).  
86 // As QDeclarativeDataLoader lives in the main thread, and we can't use
87 // Qt::DirectConnection connections from a QNetworkReply (because then 
88 // sender() wont work), we need to insert this object in the middle.
89 class QDeclarativeDataLoaderNetworkReplyProxy : public QObject
90 {
91     Q_OBJECT
92 public:
93     QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l);
94
95 public slots:
96     void finished();
97     void downloadProgress(qint64, qint64);
98
99 private:
100     QDeclarativeDataLoader *l;
101 };
102
103 class QDeclarativeDataLoaderThread : public QDeclarativeThread
104 {
105     typedef QDeclarativeDataLoaderThread This;
106
107 public:
108     QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader);
109     QNetworkAccessManager *networkAccessManager() const;
110     QDeclarativeDataLoaderNetworkReplyProxy *networkReplyProxy() const;
111
112     void load(QDeclarativeDataBlob *b);
113     void loadAsync(QDeclarativeDataBlob *b);
114     void loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &);
115     void loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &);
116     void callCompleted(QDeclarativeDataBlob *b);
117     void callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p);
118     void initializeEngine(QDeclarativeExtensionInterface *, const char *);
119
120 protected:
121     virtual void shutdownThread();
122
123 private:
124     void loadThread(QDeclarativeDataBlob *b);
125     void loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &);
126     void callCompletedMain(QDeclarativeDataBlob *b);
127     void callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p);
128     void initializeEngineMain(QDeclarativeExtensionInterface *iface, const char *uri);
129
130     QDeclarativeDataLoader *m_loader;
131     mutable QNetworkAccessManager *m_networkAccessManager;
132     mutable QDeclarativeDataLoaderNetworkReplyProxy *m_networkReplyProxy;
133 };
134
135
136 QDeclarativeDataLoaderNetworkReplyProxy::QDeclarativeDataLoaderNetworkReplyProxy(QDeclarativeDataLoader *l) 
137 : l(l) 
138 {
139 }
140
141 void QDeclarativeDataLoaderNetworkReplyProxy::finished()
142 {
143     Q_ASSERT(sender());
144     Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
145     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
146     l->networkReplyFinished(reply);
147 }
148
149 void QDeclarativeDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
150 {
151     Q_ASSERT(sender());
152     Q_ASSERT(qobject_cast<QNetworkReply *>(sender()));
153     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
154     l->networkReplyProgress(reply, bytesReceived, bytesTotal);
155 }
156
157 /*
158 Returns the set of QML files in path (qmldir, *.qml, *.js).  The caller
159 is responsible for deleting the returned data.
160 Returns 0 if the directory does not exist.
161 */
162 #if defined (Q_OS_UNIX) && !defined(Q_OS_DARWIN)
163 static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
164 {
165     QByteArray name(QFile::encodeName(path));
166     DIR *dd = opendir(name);
167     if (!dd)
168         return 0;
169
170     struct dirent *result;
171     union {
172         struct dirent d;
173         char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
174     } u;
175
176     QStringHash<bool> *files = new QStringHash<bool>;
177     while (readdir_r(dd, &u.d, &result) == 0 && result != 0) {
178         if (!strcmp(u.d.d_name, "qmldir")) {
179             files->insert(QLatin1String("qmldir"), true);
180             continue;
181         }
182         int len = strlen(u.d.d_name);
183         if (len < 4)
184             continue;
185         if (!strcmp(u.d.d_name+len-4, ".qml") || !strcmp(u.d.d_name+len-3, ".js"))
186             files->insert(QFile::decodeName(u.d.d_name), true);
187 #if defined(Q_OS_DARWIN)
188         else if ((len > 6 && !strcmp(u.d.d_name+len-6, ".dylib")) || !strcmp(u.d.d_name+len-3, ".so")
189                   || (len > 7 && !strcmp(u.d.d_name+len-7, ".bundle")))
190             files->insert(QFile::decodeName(u.d.d_name), true);
191 #else  // Unix
192         else if (!strcmp(u.d.d_name+len-3, ".so") || !strcmp(u.d.d_name+len-3, ".sl"))
193             files->insert(QFile::decodeName(u.d.d_name), true);
194 #endif
195     }
196
197     closedir(dd);
198     return files;
199 }
200 #else
201 static QStringHash<bool> *qmlFilesInDirectory(const QString &path)
202 {
203     QDirIterator dir(path, QDir::Files);
204     if (!dir.hasNext())
205         return 0;
206     QStringHash<bool> *files = new QStringHash<bool>;
207     while (dir.hasNext()) {
208         dir.next();
209         QString fileName = dir.fileName();
210         if (fileName == QLatin1String("qmldir")
211                 || fileName.endsWith(QLatin1String(".qml"))
212                 || fileName.endsWith(QLatin1String(".js"))
213 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
214                 || fileName.endsWith(QLatin1String(".dll"))
215 #elif defined(Q_OS_DARWIN)
216                 || fileName.endsWith(QLatin1String(".dylib"))
217                 || fileName.endsWith(QLatin1String(".so"))
218                 || fileName.endsWith(QLatin1String(".bundle"))
219 #else  // Unix
220                 || fileName.endsWith(QLatin1String(".so"))
221                 || fileName.endsWith(QLatin1String(".sl"))
222 #endif
223                 ) {
224 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
225             fileName = fileName.toLower();
226 #endif
227             files->insert(fileName, true);
228         }
229     }
230     return files;
231 }
232 #endif
233
234
235 /*!
236 \class QDeclarativeDataBlob
237 \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader.
238 \internal
239
240 QDeclarativeDataBlob's are loaded by a QDeclarativeDataLoader.  The user creates the QDeclarativeDataBlob
241 and then calls QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() to load it.
242 The QDeclarativeDataLoader invokes callbacks on the QDeclarativeDataBlob as data becomes available.
243 */
244
245 /*!
246 \enum QDeclarativeDataBlob::Status
247
248 This enum describes the status of the data blob.
249
250 \list
251 \o Null The blob has not yet been loaded by a QDeclarativeDataLoader
252 \o Loading The blob is loading network data.  The QDeclarativeDataBlob::setData() callback has not yet been
253 invoked or has not yet returned.
254 \o WaitingForDependencies The blob is waiting for dependencies to be done before continueing.  This status
255 only occurs after the QDeclarativeDataBlob::setData() callback has been made, and when the blob has outstanding
256 dependencies.
257 \o Complete The blob's data has been loaded and all dependencies are done.
258 \o Error An error has been set on this blob.
259 \endlist
260 */
261
262 /*!
263 \enum QDeclarativeDataBlob::Type
264
265 This enum describes the type of the data blob.
266
267 \list
268 \o QmlFile This is a QDeclarativeTypeData
269 \o JavaScriptFile This is a QDeclarativeScriptData
270 \o QmldirFile This is a QDeclarativeQmldirData
271 \endlist
272 */
273
274 /*!
275 Create a new QDeclarativeDataBlob for \a url and of the provided \a type.
276 */
277 QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type)
278 : m_type(type), m_url(url), m_finalUrl(url), m_manager(0), m_redirectCount(0), 
279   m_inCallback(false), m_isDone(false)
280 {
281 }
282
283 /*!  \internal */
284 QDeclarativeDataBlob::~QDeclarativeDataBlob()
285 {
286     Q_ASSERT(m_waitingOnMe.isEmpty());
287
288     cancelAllWaitingFor();
289 }
290
291 /*!
292 Returns the type provided to the constructor.
293 */
294 QDeclarativeDataBlob::Type QDeclarativeDataBlob::type() const
295 {
296     return m_type;
297 }
298
299 /*!
300 Returns the blob's status.
301 */
302 QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
303 {
304     return m_data.status();
305 }
306
307 /*!
308 Returns true if the status is Null.
309 */
310 bool QDeclarativeDataBlob::isNull() const
311 {
312     return status() == Null;
313 }
314
315 /*!
316 Returns true if the status is Loading.
317 */
318 bool QDeclarativeDataBlob::isLoading() const
319 {
320     return status() == Loading;
321 }
322
323 /*!
324 Returns true if the status is WaitingForDependencies.
325 */
326 bool QDeclarativeDataBlob::isWaiting() const
327 {
328     return status() == WaitingForDependencies;
329 }
330
331 /*!
332 Returns true if the status is Complete.
333 */
334 bool QDeclarativeDataBlob::isComplete() const
335 {
336     return status() == Complete;
337 }
338
339 /*!
340 Returns true if the status is Error.
341 */
342 bool QDeclarativeDataBlob::isError() const
343 {
344     return status() == Error;
345 }
346
347 /*!
348 Returns true if the status is Complete or Error.
349 */
350 bool QDeclarativeDataBlob::isCompleteOrError() const
351 {
352     Status s = status();
353     return s == Error || s == Complete;
354 }
355
356 /*!
357 Returns the data download progress from 0 to 1.
358 */
359 qreal QDeclarativeDataBlob::progress() const
360 {
361     quint8 p = m_data.progress();
362     if (p == 0xFF) return 1.;
363     else return qreal(p) / qreal(0xFF);
364 }
365
366 /*!
367 Returns the blob url passed to the constructor.  If a network redirect
368 happens while fetching the data, this url remains the same.
369
370 \sa finalUrl()
371 */
372 QUrl QDeclarativeDataBlob::url() const
373 {
374     return m_url;
375 }
376
377 /*!
378 Returns the final url of the data.  Initially this is the same as
379 url(), but if a network redirect happens while fetching the data, this url
380 is updated to reflect the new location.
381
382 May only be called from the load thread, or after the blob isCompleteOrError().
383 */
384 QUrl QDeclarativeDataBlob::finalUrl() const
385 {
386     Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
387     return m_finalUrl;
388 }
389
390 /*!
391 Return the errors on this blob.
392
393 May only be called from the load thread, or after the blob isCompleteOrError().
394 */
395 QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
396 {
397     Q_ASSERT(isCompleteOrError() || (m_manager && m_manager->m_thread->isThisThread()));
398     return m_errors;
399 }
400
401 /*!
402 Mark this blob as having \a errors.
403
404 All outstanding dependencies will be cancelled.  Requests to add new dependencies 
405 will be ignored.  Entry into the Error state is irreversable.
406
407 The setError() method may only be called from within a QDeclarativeDataBlob callback.
408 */
409 void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
410 {
411     ASSERT_CALLBACK();
412
413     QList<QDeclarativeError> l;
414     l << errors;
415     setError(l);
416 }
417
418 /*!
419 \overload
420 */
421 void QDeclarativeDataBlob::setError(const QList<QDeclarativeError> &errors)
422 {
423     ASSERT_CALLBACK();
424
425     Q_ASSERT(status() != Error);
426     Q_ASSERT(m_errors.isEmpty());
427
428     m_errors = errors; // Must be set before the m_data fence
429     m_data.setStatus(Error);
430
431     cancelAllWaitingFor();
432
433     if (!m_inCallback)
434         tryDone();
435 }
436
437 /*! 
438 Wait for \a blob to become complete or to error.  If \a blob is already 
439 complete or in error, or this blob is already complete, this has no effect.
440
441 The setError() method may only be called from within a QDeclarativeDataBlob callback.
442 */
443 void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob)
444 {
445     ASSERT_CALLBACK();
446
447     Q_ASSERT(status() != Null);
448
449     if (!blob ||
450         blob->status() == Error || blob->status() == Complete ||
451         status() == Error || status() == Complete || m_isDone || 
452         m_waitingFor.contains(blob))
453         return;
454
455     blob->addref();
456
457     m_data.setStatus(WaitingForDependencies);
458
459     m_waitingFor.append(blob);
460     blob->m_waitingOnMe.append(this);
461 }
462
463 /*!
464 \fn void QDeclarativeDataBlob::dataReceived(const QByteArray &data)
465
466 Invoked when data for the blob is received.  Implementors should use this callback
467 to determine a blob's dependencies.  Within this callback you may call setError()
468 or addDependency().
469 */
470
471 /*!
472 Invoked once data has either been received or a network error occurred, and all 
473 dependencies are complete.
474
475 You can set an error in this method, but you cannot add new dependencies.  Implementors
476 should use this callback to finalize processing of data.
477
478 The default implementation does nothing.
479
480 XXX Rename processData() or some such to avoid confusion between done() (processing thread)
481 and completed() (main thread)
482 */
483 void QDeclarativeDataBlob::done()
484 {
485 }
486
487 /*!
488 Invoked if there is a network error while fetching this blob.
489
490 The default implementation sets an appropriate QDeclarativeError.
491 */
492 void QDeclarativeDataBlob::networkError(QNetworkReply::NetworkError networkError)
493 {
494     Q_UNUSED(networkError);
495
496     QDeclarativeError error;
497     error.setUrl(m_finalUrl);
498
499     const char *errorString = 0;
500     switch (networkError) {
501         default:
502             errorString = "Network error";
503             break;
504         case QNetworkReply::ConnectionRefusedError:
505             errorString = "Connection refused";
506             break;
507         case QNetworkReply::RemoteHostClosedError:
508             errorString = "Remote host closed the connection";
509             break;
510         case QNetworkReply::HostNotFoundError:
511             errorString = "Host not found";
512             break;
513         case QNetworkReply::TimeoutError:
514             errorString = "Timeout";
515             break;
516         case QNetworkReply::ProxyConnectionRefusedError:
517         case QNetworkReply::ProxyConnectionClosedError:
518         case QNetworkReply::ProxyNotFoundError:
519         case QNetworkReply::ProxyTimeoutError:
520         case QNetworkReply::ProxyAuthenticationRequiredError:
521         case QNetworkReply::UnknownProxyError:
522             errorString = "Proxy error";
523             break;
524         case QNetworkReply::ContentAccessDenied:
525             errorString = "Access denied";
526             break;
527         case QNetworkReply::ContentNotFoundError:
528             errorString = "File not found";
529             break;
530         case QNetworkReply::AuthenticationRequiredError:
531             errorString = "Authentication required";
532             break;
533     };
534
535     error.setDescription(QLatin1String(errorString));
536
537     setError(error);
538 }
539
540 /*! 
541 Called if \a blob, which was previously waited for, has an error.
542
543 The default implementation does nothing.
544 */
545 void QDeclarativeDataBlob::dependencyError(QDeclarativeDataBlob *blob)
546 {
547     Q_UNUSED(blob);
548 }
549
550 /*!
551 Called if \a blob, which was previously waited for, has completed.
552
553 The default implementation does nothing.
554 */
555 void QDeclarativeDataBlob::dependencyComplete(QDeclarativeDataBlob *blob)
556 {
557     Q_UNUSED(blob);
558 }
559
560 /*! 
561 Called when all blobs waited for have completed.  This occurs regardless of 
562 whether they are in error, or complete state.  
563
564 The default implementation does nothing.
565 */
566 void QDeclarativeDataBlob::allDependenciesDone()
567 {
568 }
569
570 /*!
571 Called when the download progress of this blob changes.  \a progress goes
572 from 0 to 1.
573
574 This callback is only invoked if an asynchronous load for this blob is 
575 made.  An asynchronous load is one in which the Asynchronous mode is
576 specified explicitly, or one that is implicitly delayed due to a network 
577 operation.
578
579 The default implementation does nothing.
580 */
581 void QDeclarativeDataBlob::downloadProgressChanged(qreal progress)
582 {
583     Q_UNUSED(progress);
584 }
585
586 /*!
587 Invoked on the main thread sometime after done() was called on the load thread.
588
589 You cannot modify the blobs state at all in this callback and cannot depend on the
590 order or timeliness of these callbacks.  Implementors should use this callback to notify 
591 dependencies on the main thread that the blob is done and not a lot else.
592
593 This callback is only invoked if an asynchronous load for this blob is 
594 made.  An asynchronous load is one in which the Asynchronous mode is
595 specified explicitly, or one that is implicitly delayed due to a network 
596 operation.
597
598 The default implementation does nothing.
599 */
600 void QDeclarativeDataBlob::completed()
601 {
602 }
603
604
605 void QDeclarativeDataBlob::tryDone()
606 {
607     if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
608         m_isDone = true;
609         addref();
610
611 #ifdef DATABLOB_DEBUG
612         qWarning("QDeclarativeDataBlob::done() %s", qPrintable(url().toString()));
613 #endif
614         done();
615
616         if (status() != Error)
617             m_data.setStatus(Complete);
618
619         notifyAllWaitingOnMe();
620
621         // Locking is not required here, as anyone expecting callbacks must
622         // already be protected against the blob being completed (as set above);
623         if (m_data.isAsync()) {
624 #ifdef DATABLOB_DEBUG
625             qWarning("QDeclarativeDataBlob: Dispatching completed");
626 #endif
627             m_manager->m_thread->callCompleted(this);
628         }
629
630         release();
631     }
632 }
633
634 void QDeclarativeDataBlob::cancelAllWaitingFor()
635 {
636     while (m_waitingFor.count()) {
637         QDeclarativeDataBlob *blob = m_waitingFor.takeLast();
638
639         Q_ASSERT(blob->m_waitingOnMe.contains(this));
640
641         blob->m_waitingOnMe.removeOne(this);
642
643         blob->release();
644     }
645 }
646
647 void QDeclarativeDataBlob::notifyAllWaitingOnMe()
648 {
649     while (m_waitingOnMe.count()) {
650         QDeclarativeDataBlob *blob = m_waitingOnMe.takeLast();
651
652         Q_ASSERT(blob->m_waitingFor.contains(this));
653
654         blob->notifyComplete(this);
655     }
656 }
657
658 void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
659 {
660     Q_ASSERT(m_waitingFor.contains(blob));
661     Q_ASSERT(blob->status() == Error || blob->status() == Complete);
662
663     m_inCallback = true;
664
665     if (blob->status() == Error) {
666         dependencyError(blob);
667     } else if (blob->status() == Complete) {
668         dependencyComplete(blob);
669     }
670
671     m_waitingFor.removeOne(blob);
672     blob->release();
673
674     if (!isError() && m_waitingFor.isEmpty()) 
675         allDependenciesDone();
676
677     m_inCallback = false;
678
679     tryDone();
680 }
681
682 #define TD_STATUS_MASK 0x0000FFFF
683 #define TD_STATUS_SHIFT 0
684 #define TD_PROGRESS_MASK 0x00FF0000
685 #define TD_PROGRESS_SHIFT 16
686 #define TD_ASYNC_MASK 0x80000000
687
688 QDeclarativeDataBlob::ThreadData::ThreadData()
689 : _p(0)
690 {
691 }
692
693 QDeclarativeDataBlob::Status QDeclarativeDataBlob::ThreadData::status() const
694 {
695     return QDeclarativeDataBlob::Status((_p.load() & TD_STATUS_MASK) >> TD_STATUS_SHIFT);
696 }
697
698 void QDeclarativeDataBlob::ThreadData::setStatus(QDeclarativeDataBlob::Status status)
699 {
700     while (true) {
701         int d = _p.load();
702         int nd = (d & ~TD_STATUS_MASK) | ((status << TD_STATUS_SHIFT) & TD_STATUS_MASK);
703         if (d == nd || _p.testAndSetOrdered(d, nd)) return;
704     }
705 }
706
707 bool QDeclarativeDataBlob::ThreadData::isAsync() const
708 {
709     return _p.load() & TD_ASYNC_MASK;
710 }
711
712 void QDeclarativeDataBlob::ThreadData::setIsAsync(bool v)
713 {
714     while (true) {
715         int d = _p.load();
716         int nd = (d & ~TD_ASYNC_MASK) | (v?TD_ASYNC_MASK:0);
717         if (d == nd || _p.testAndSetOrdered(d, nd)) return;
718     }
719 }
720
721 quint8 QDeclarativeDataBlob::ThreadData::progress() const
722 {
723     return quint8((_p.load() & TD_PROGRESS_MASK) >> TD_PROGRESS_SHIFT);
724 }
725
726 void QDeclarativeDataBlob::ThreadData::setProgress(quint8 v)
727 {
728     while (true) {
729         int d = _p.load();
730         int nd = (d & ~TD_PROGRESS_MASK) | ((v << TD_PROGRESS_SHIFT) & TD_PROGRESS_MASK);
731         if (d == nd || _p.testAndSetOrdered(d, nd)) return;
732     }
733 }
734
735 QDeclarativeDataLoaderThread::QDeclarativeDataLoaderThread(QDeclarativeDataLoader *loader)
736 : m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
737 {
738 }
739
740 QNetworkAccessManager *QDeclarativeDataLoaderThread::networkAccessManager() const
741 {
742     Q_ASSERT(isThisThread());
743     if (!m_networkAccessManager) {
744         m_networkAccessManager = QDeclarativeEnginePrivate::get(m_loader->engine())->createNetworkAccessManager(0);
745         m_networkReplyProxy = new QDeclarativeDataLoaderNetworkReplyProxy(m_loader);
746     }
747
748     return m_networkAccessManager;
749 }
750
751 QDeclarativeDataLoaderNetworkReplyProxy *QDeclarativeDataLoaderThread::networkReplyProxy() const
752 {
753     Q_ASSERT(isThisThread());
754     Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
755     return m_networkReplyProxy;
756 }
757
758 void QDeclarativeDataLoaderThread::load(QDeclarativeDataBlob *b) 
759
760     b->addref();
761     callMethodInThread(&This::loadThread, b); 
762 }
763
764 void QDeclarativeDataLoaderThread::loadAsync(QDeclarativeDataBlob *b)
765 {
766     b->addref();
767     postMethodToThread(&This::loadThread, b);
768 }
769
770 void QDeclarativeDataLoaderThread::loadWithStaticData(QDeclarativeDataBlob *b, const QByteArray &d)
771 {
772     b->addref();
773     callMethodInThread(&This::loadWithStaticDataThread, b, d);
774 }
775
776 void QDeclarativeDataLoaderThread::loadWithStaticDataAsync(QDeclarativeDataBlob *b, const QByteArray &d)
777 {
778     b->addref();
779     postMethodToThread(&This::loadWithStaticDataThread, b, d);
780 }
781
782 void QDeclarativeDataLoaderThread::callCompleted(QDeclarativeDataBlob *b) 
783
784     b->addref(); 
785     postMethodToMain(&This::callCompletedMain, b); 
786 }
787
788 void QDeclarativeDataLoaderThread::callDownloadProgressChanged(QDeclarativeDataBlob *b, qreal p) 
789
790     b->addref(); 
791     postMethodToMain(&This::callDownloadProgressChangedMain, b, p); 
792 }
793
794 void QDeclarativeDataLoaderThread::initializeEngine(QDeclarativeExtensionInterface *iface, 
795                                                     const char *uri)
796 {
797     callMethodInMain(&This::initializeEngineMain, iface, uri);
798 }
799
800 void QDeclarativeDataLoaderThread::shutdownThread()
801 {
802     delete m_networkAccessManager;
803     m_networkAccessManager = 0;
804     delete m_networkReplyProxy;
805     m_networkReplyProxy = 0;
806 }
807
808 void QDeclarativeDataLoaderThread::loadThread(QDeclarativeDataBlob *b) 
809
810     m_loader->loadThread(b); 
811     b->release();
812 }
813
814 void QDeclarativeDataLoaderThread::loadWithStaticDataThread(QDeclarativeDataBlob *b, const QByteArray &d)
815 {
816     m_loader->loadWithStaticDataThread(b, d);
817     b->release();
818 }
819
820 void QDeclarativeDataLoaderThread::callCompletedMain(QDeclarativeDataBlob *b) 
821
822 #ifdef DATABLOB_DEBUG
823     qWarning("QDeclarativeDataLoaderThread: %s completed() callback", qPrintable(b->url().toString())); 
824 #endif
825     b->completed(); 
826     b->release(); 
827 }
828
829 void QDeclarativeDataLoaderThread::callDownloadProgressChangedMain(QDeclarativeDataBlob *b, qreal p) 
830
831 #ifdef DATABLOB_DEBUG
832     qWarning("QDeclarativeDataLoaderThread: %s downloadProgressChanged(%f) callback", 
833              qPrintable(b->url().toString()), p); 
834 #endif
835     b->downloadProgressChanged(p); 
836     b->release(); 
837 }
838
839 void QDeclarativeDataLoaderThread::initializeEngineMain(QDeclarativeExtensionInterface *iface, 
840                                                         const char *uri)
841 {
842     Q_ASSERT(m_loader->engine()->thread() == QThread::currentThread());
843     iface->initializeEngine(m_loader->engine(), uri);
844 }
845
846 /*!
847 \class QDeclarativeDataLoader
848 \brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network.
849 \internal
850
851 The QDeclarativeDataLoader class is provided for the exclusive use of the QDeclarativeTypeLoader class.
852
853 Clients create QDeclarativeDataBlob instances and submit them to the QDeclarativeDataLoader class
854 through the QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() methods.
855 The loader then fetches the data over the network or from the local file system in an efficient way.
856 QDeclarativeDataBlob is an abstract class, so should always be specialized.
857
858 Once data is received, the QDeclarativeDataBlob::dataReceived() method is invoked on the blob.  The
859 derived class should use this callback to process the received data.  Processing of the data can 
860 result in an error being set (QDeclarativeDataBlob::setError()), or one or more dependencies being
861 created (QDeclarativeDataBlob::addDependency()).  Dependencies are other QDeclarativeDataBlob's that
862 are required before processing can fully complete.
863
864 To complete processing, the QDeclarativeDataBlob::done() callback is invoked.  done() is called when
865 one of these three preconditions are met.
866
867 \list 1
868 \o The QDeclarativeDataBlob has no dependencies.
869 \o The QDeclarativeDataBlob has an error set.
870 \o All the QDeclarativeDataBlob's dependencies are themselves "done()".
871 \endlist
872
873 Thus QDeclarativeDataBlob::done() will always eventually be called, even if the blob has an error set.
874 */
875
876 /*!
877 Create a new QDeclarativeDataLoader for \a engine.
878 */
879 QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
880 : m_engine(engine), m_thread(new QDeclarativeDataLoaderThread(this))
881 {
882 }
883
884 /*! \internal */
885 QDeclarativeDataLoader::~QDeclarativeDataLoader()
886 {
887     for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) 
888         (*iter)->release();
889
890     m_thread->shutdown();
891     delete m_thread;
892 }
893
894 void QDeclarativeDataLoader::lock()
895 {
896     m_thread->lock();
897 }
898
899 void QDeclarativeDataLoader::unlock()
900 {
901     m_thread->unlock();
902 }
903
904 /*!
905 Load the provided \a blob from the network or filesystem.
906
907 The loader must be locked.
908 */
909 void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob, Mode mode)
910 {
911 #ifdef DATABLOB_DEBUG
912     qWarning("QDeclarativeDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()), 
913              m_thread->isThisThread()?"Compile":"Engine");
914 #endif
915
916     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
917     Q_ASSERT(blob->m_manager == 0);
918
919     blob->m_data.setStatus(QDeclarativeDataBlob::Loading);
920     blob->m_manager = this;
921
922     if (m_thread->isThisThread()) {
923         unlock();
924         loadThread(blob);
925         lock();
926     } else if (mode == PreferSynchronous) {
927         unlock();
928         m_thread->load(blob);
929         lock();
930         if (!blob->isCompleteOrError())
931             blob->m_data.setIsAsync(true);
932     } else {
933         Q_ASSERT(mode == Asynchronous);
934         blob->m_data.setIsAsync(true);
935         unlock();
936         m_thread->loadAsync(blob);
937         lock();
938     }
939 }
940
941 /*!
942 Load the provided \a blob with \a data.  The blob's URL is not used by the data loader in this case.
943
944 The loader must be locked.
945 */
946 void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data, Mode mode)
947 {
948 #ifdef DATABLOB_DEBUG
949     qWarning("QDeclarativeDataLoader::loadWithStaticData(%s, data): %s thread", qPrintable(blob->m_url.toString()), 
950              m_thread->isThisThread()?"Compile":"Engine");
951 #endif
952
953     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
954     Q_ASSERT(blob->m_manager == 0);
955     
956     blob->m_data.setStatus(QDeclarativeDataBlob::Loading);
957     blob->m_manager = this;
958
959     if (m_thread->isThisThread()) {
960         unlock();
961         loadWithStaticDataThread(blob, data);
962         lock();
963     } else if (mode == PreferSynchronous) {
964         unlock();
965         m_thread->loadWithStaticData(blob, data);
966         lock();
967         if (!blob->isCompleteOrError())
968             blob->m_data.setIsAsync(true);
969     } else {
970         Q_ASSERT(mode == Asynchronous);
971         blob->m_data.setIsAsync(true);
972         unlock();
973         m_thread->loadWithStaticDataAsync(blob, data);
974         lock();
975     }
976 }
977
978 void QDeclarativeDataLoader::loadWithStaticDataThread(QDeclarativeDataBlob *blob, const QByteArray &data)
979 {
980     ASSERT_LOADTHREAD();
981
982     setData(blob, data);
983 }
984
985 void QDeclarativeDataLoader::loadThread(QDeclarativeDataBlob *blob)
986 {
987     ASSERT_LOADTHREAD();
988
989     if (blob->m_url.isEmpty()) {
990         QDeclarativeError error;
991         error.setDescription(QLatin1String("Invalid null URL"));
992         blob->setError(error);
993         return;
994     }
995
996     QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
997
998     if (!lf.isEmpty()) {
999         if (!QDeclarative_isFileCaseCorrect(lf)) {
1000             QDeclarativeError error;
1001             error.setUrl(blob->m_url);
1002             error.setDescription(QLatin1String("File name case mismatch"));
1003             blob->setError(error);
1004             return;
1005         }
1006         QFile file(lf);
1007         if (file.open(QFile::ReadOnly)) {
1008             QByteArray data = file.readAll();
1009
1010             blob->m_data.setProgress(0xFF);
1011             if (blob->m_data.isAsync())
1012                 m_thread->callDownloadProgressChanged(blob, 1.);
1013
1014             setData(blob, data);
1015         } else {
1016             blob->networkError(QNetworkReply::ContentNotFoundError);
1017         }
1018
1019     } else {
1020
1021         QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
1022         QObject *nrp = m_thread->networkReplyProxy();
1023         QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), 
1024                          nrp, SLOT(downloadProgress(qint64,qint64)));
1025         QObject::connect(reply, SIGNAL(finished()), 
1026                          nrp, SLOT(finished()));
1027         m_networkReplies.insert(reply, blob);
1028
1029         blob->addref();
1030     }
1031 }
1032
1033 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
1034
1035 void QDeclarativeDataLoader::networkReplyFinished(QNetworkReply *reply)
1036 {
1037     Q_ASSERT(m_thread->isThisThread());
1038
1039     reply->deleteLater();
1040
1041     QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
1042
1043     Q_ASSERT(blob);
1044
1045     blob->m_redirectCount++;
1046
1047     if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
1048         QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
1049         if (redirect.isValid()) {
1050             QUrl url = reply->url().resolved(redirect.toUrl());
1051             blob->m_finalUrl = url;
1052
1053             QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url));
1054             QObject *nrp = m_thread->networkReplyProxy();
1055             QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished()));
1056             m_networkReplies.insert(reply, blob);
1057             return;
1058         }
1059     }
1060
1061     if (reply->error()) {
1062         blob->networkError(reply->error());
1063     } else {
1064         QByteArray data = reply->readAll();
1065         setData(blob, data);
1066     }
1067
1068     blob->release();
1069 }
1070
1071 void QDeclarativeDataLoader::networkReplyProgress(QNetworkReply *reply,
1072                                                   qint64 bytesReceived, qint64 bytesTotal)
1073 {
1074     Q_ASSERT(m_thread->isThisThread());
1075
1076     QDeclarativeDataBlob *blob = m_networkReplies.value(reply);
1077
1078     Q_ASSERT(blob);
1079
1080     if (bytesTotal != 0) {
1081         quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal));
1082         blob->m_data.setProgress(progress);
1083         if (blob->m_data.isAsync())
1084             m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
1085     }
1086 }
1087
1088 /*!
1089 Return the QDeclarativeEngine associated with this loader
1090 */
1091 QDeclarativeEngine *QDeclarativeDataLoader::engine() const
1092 {
1093     return m_engine;
1094 }
1095
1096 /*!
1097 Call the initializeEngine() method on \a iface.  Used by QDeclarativeImportDatabase to ensure it
1098 gets called in the correct thread.
1099 */
1100 void QDeclarativeDataLoader::initializeEngine(QDeclarativeExtensionInterface *iface, 
1101                                               const char *uri)
1102 {
1103     Q_ASSERT(m_thread->isThisThread() || engine()->thread() == QThread::currentThread());
1104
1105     if (m_thread->isThisThread()) {
1106         m_thread->initializeEngine(iface, uri);
1107     } else {
1108         Q_ASSERT(engine()->thread() == QThread::currentThread());
1109         iface->initializeEngine(engine(), uri);
1110     }
1111 }
1112
1113
1114 void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
1115 {
1116     blob->m_inCallback = true;
1117
1118     blob->dataReceived(data);
1119
1120     if (!blob->isError() && !blob->isWaiting())
1121         blob->allDependenciesDone();
1122
1123     if (blob->status() != QDeclarativeDataBlob::Error) 
1124         blob->m_data.setStatus(QDeclarativeDataBlob::WaitingForDependencies);
1125
1126     blob->m_inCallback = false;
1127
1128     blob->tryDone();
1129 }
1130
1131 /*!
1132 Constructs a new type loader that uses the given \a engine.
1133 */
1134 QDeclarativeTypeLoader::QDeclarativeTypeLoader(QDeclarativeEngine *engine)
1135 : QDeclarativeDataLoader(engine)
1136 {
1137 }
1138
1139 /*!
1140 Destroys the type loader, first clearing the cache of any information about
1141 loaded files.
1142 */
1143 QDeclarativeTypeLoader::~QDeclarativeTypeLoader()
1144 {
1145     clearCache();
1146 }
1147
1148 /*!
1149 \enum QDeclarativeTypeLoader::Option
1150
1151 This enum defines the options that control the way type data is handled.
1152
1153 \value None             The default value, indicating that no other options
1154                         are enabled.
1155 \value PreserveParser   The parser used to handle the type data is preserved
1156                         after the data has been parsed.
1157 */
1158
1159 /*!
1160 Returns a QDeclarativeTypeData for the specified \a url.  The QDeclarativeTypeData may be cached.
1161 */
1162 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
1163 {
1164     Q_ASSERT(!url.isRelative() && 
1165             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
1166              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
1167
1168     lock();
1169     
1170     QDeclarativeTypeData *typeData = m_typeCache.value(url);
1171
1172     if (!typeData) {
1173         typeData = new QDeclarativeTypeData(url, None, this);
1174         m_typeCache.insert(url, typeData);
1175         QDeclarativeDataLoader::load(typeData);
1176     }
1177
1178     typeData->addref();
1179
1180     unlock();
1181
1182     return typeData;
1183 }
1184
1185 /*!
1186 Returns a QDeclarativeTypeData for the given \a data with the provided base \a url.  The 
1187 QDeclarativeTypeData will not be cached.
1188
1189 The specified \a options control how the loader handles type data.
1190 */
1191 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
1192 {
1193     lock();
1194
1195     QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
1196     QDeclarativeDataLoader::loadWithStaticData(typeData, data);
1197
1198     unlock();
1199
1200     return typeData;
1201 }
1202
1203 /*!
1204 Return a QDeclarativeScriptBlob for \a url.  The QDeclarativeScriptData may be cached.
1205 */
1206 QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
1207 {
1208     Q_ASSERT(!url.isRelative() && 
1209             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
1210              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
1211
1212     lock();
1213
1214     QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
1215
1216     if (!scriptBlob) {
1217         scriptBlob = new QDeclarativeScriptBlob(url, this);
1218         m_scriptCache.insert(url, scriptBlob);
1219         QDeclarativeDataLoader::load(scriptBlob);
1220     }
1221
1222     scriptBlob->addref();
1223
1224     unlock();
1225
1226     return scriptBlob;
1227 }
1228
1229 /*!
1230 Returns a QDeclarativeQmldirData for \a url.  The QDeclarativeQmldirData may be cached.
1231 */
1232 QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
1233 {
1234     Q_ASSERT(!url.isRelative() && 
1235             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
1236              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
1237
1238     lock();
1239
1240     QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
1241
1242     if (!qmldirData) {
1243         qmldirData = new QDeclarativeQmldirData(url);
1244         m_qmldirCache.insert(url, qmldirData);
1245         QDeclarativeDataLoader::load(qmldirData);
1246     }
1247
1248     qmldirData->addref();
1249
1250     unlock();
1251
1252     return qmldirData;
1253 }
1254
1255 /*!
1256 Returns the absolute filename of path via a directory cache for files named
1257 "qmldir", "*.qml", "*.js", and plugins.
1258 Returns a empty string if the path does not exist.
1259 */
1260 QString QDeclarativeTypeLoader::absoluteFilePath(const QString &path)
1261 {
1262     if (path.isEmpty())
1263         return QString();
1264     if (path.at(0) == QLatin1Char(':')) {
1265         // qrc resource
1266         QFileInfo fileInfo(path);
1267         return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString();
1268     }
1269 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1270     QString lowPath = path.toLower();
1271     int lastSlash = lowPath.lastIndexOf(QLatin1Char('/'));
1272     QString dirPath = lowPath.left(lastSlash);
1273 #else
1274     int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1275     QStringRef dirPath(&path, 0, lastSlash);
1276 #endif
1277
1278     StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1279     if (!fileSet) {
1280 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1281         QHashedString dirPathString(dirPath);
1282 #else
1283         QHashedString dirPathString(dirPath.toString());
1284 #endif
1285         StringSet *files = qmlFilesInDirectory(dirPathString);
1286         m_importDirCache.insert(dirPathString, files);
1287         fileSet = m_importDirCache.value(dirPathString);
1288     }
1289     if (!(*fileSet))
1290         return QString();
1291
1292 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1293     QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(lowPath.constData()+lastSlash+1, lowPath.length()-lastSlash-1)) ? path : QString();
1294 #else
1295     QString absoluteFilePath = (*fileSet)->contains(QHashedStringRef(path.constData()+lastSlash+1, path.length()-lastSlash-1)) ? path : QString();
1296 #endif
1297     if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':'))
1298         absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath();
1299
1300     return absoluteFilePath;
1301 }
1302
1303 /*!
1304 Returns true if the path is a directory via a directory cache.  Cache is
1305 shared with absoluteFilePath().
1306 */
1307 bool QDeclarativeTypeLoader::directoryExists(const QString &path)
1308 {
1309     if (path.isEmpty())
1310         return false;
1311     if (path.at(0) == QLatin1Char(':')) {
1312         // qrc resource
1313         QFileInfo fileInfo(path);
1314         return fileInfo.exists() && fileInfo.isDir();
1315     }
1316
1317     int length = path.length();
1318     if (path.endsWith(QLatin1Char('/')))
1319         --length;
1320 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1321     QString dirPath = path.left(length).toLower();
1322 #else
1323     QStringRef dirPath(&path, 0, length);
1324 #endif
1325
1326     StringSet **fileSet = m_importDirCache.value(QHashedStringRef(dirPath.constData(), dirPath.length()));
1327     if (!fileSet) {
1328 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_DARWIN)
1329         QHashedString dirPathString(dirPath);
1330 #else
1331         QHashedString dirPathString(dirPath.toString());
1332 #endif
1333         StringSet *files = qmlFilesInDirectory(dirPathString);
1334         m_importDirCache.insert(dirPathString, files);
1335         fileSet = m_importDirCache.value(dirPathString);
1336     }
1337
1338     return (*fileSet);
1339 }
1340
1341
1342 /*!
1343 Return a QDeclarativeDirParser for absoluteFilePath.  The QDeclarativeDirParser may be cached.
1344 */
1345 const QDeclarativeDirParser *QDeclarativeTypeLoader::qmlDirParser(const QString &absoluteFilePath)
1346 {
1347     QDeclarativeDirParser *qmldirParser;
1348     QDeclarativeDirParser **val = m_importQmlDirCache.value(absoluteFilePath);
1349     if (!val) {
1350         qmldirParser = new QDeclarativeDirParser;
1351         qmldirParser->setFileSource(absoluteFilePath);
1352         qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath));
1353         qmldirParser->parse();
1354         m_importQmlDirCache.insert(absoluteFilePath, qmldirParser);
1355     } else {
1356         qmldirParser = *val;
1357     }
1358
1359     return qmldirParser;
1360 }
1361
1362
1363 /*!
1364 Clears cached information about loaded files, including any type data, scripts
1365 and qmldir information.
1366 */
1367 void QDeclarativeTypeLoader::clearCache()
1368 {
1369     for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) 
1370         (*iter)->release();
1371     for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) 
1372         (*iter)->release();
1373     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) 
1374         (*iter)->release();
1375     qDeleteAll(m_importDirCache);
1376     qDeleteAll(m_importQmlDirCache);
1377
1378     m_typeCache.clear();
1379     m_scriptCache.clear();
1380     m_qmldirCache.clear();
1381     m_importDirCache.clear();
1382     m_importQmlDirCache.clear();
1383 }
1384
1385
1386 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, 
1387                                            QDeclarativeTypeLoader *manager)
1388 : QDeclarativeDataBlob(url, QmlFile), m_options(options), m_imports(manager), m_typesResolved(false),
1389    m_compiledData(0), m_typeLoader(manager)
1390 {
1391 }
1392
1393 QDeclarativeTypeData::~QDeclarativeTypeData()
1394 {
1395     for (int ii = 0; ii < m_scripts.count(); ++ii) 
1396         m_scripts.at(ii).script->release();
1397     for (int ii = 0; ii < m_qmldirs.count(); ++ii) 
1398         m_qmldirs.at(ii)->release();
1399     for (int ii = 0; ii < m_types.count(); ++ii) 
1400         if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
1401     if (m_compiledData)
1402         m_compiledData->release();
1403 }
1404
1405 QDeclarativeTypeLoader *QDeclarativeTypeData::typeLoader() const
1406 {
1407     return m_typeLoader;
1408 }
1409
1410 const QDeclarativeImports &QDeclarativeTypeData::imports() const
1411 {
1412     return m_imports;
1413 }
1414
1415 const QDeclarativeScript::Parser &QDeclarativeTypeData::parser() const
1416 {
1417     return scriptParser;
1418 }
1419
1420 const QList<QDeclarativeTypeData::TypeReference> &QDeclarativeTypeData::resolvedTypes() const
1421 {
1422     return m_types;
1423 }
1424
1425 const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolvedScripts() const
1426 {
1427     return m_scripts;
1428 }
1429
1430 const QSet<QString> &QDeclarativeTypeData::namespaces() const
1431 {
1432     return m_namespaces;
1433 }
1434
1435 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
1436 {
1437     if (m_compiledData) 
1438         m_compiledData->addref();
1439
1440     return m_compiledData;
1441 }
1442
1443 void QDeclarativeTypeData::registerCallback(TypeDataCallback *callback)
1444 {
1445     Q_ASSERT(!m_callbacks.contains(callback));
1446     m_callbacks.append(callback);
1447 }
1448
1449 void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
1450 {
1451     Q_ASSERT(m_callbacks.contains(callback));
1452     m_callbacks.removeOne(callback);
1453     Q_ASSERT(!m_callbacks.contains(callback));
1454 }
1455
1456 void QDeclarativeTypeData::done()
1457 {
1458     // Check all script dependencies for errors
1459     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1460         const ScriptReference &script = m_scripts.at(ii);
1461         Q_ASSERT(script.script->isCompleteOrError());
1462         if (script.script->isError()) {
1463             QList<QDeclarativeError> errors = script.script->errors();
1464             QDeclarativeError error;
1465             error.setUrl(finalUrl());
1466             error.setLine(script.location.line);
1467             error.setColumn(script.location.column);
1468             error.setDescription(QDeclarativeTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
1469             errors.prepend(error);
1470             setError(errors);
1471         }
1472     }
1473
1474     // Check all type dependencies for errors
1475     for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
1476         const TypeReference &type = m_types.at(ii);
1477         Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
1478         if (type.typeData && type.typeData->isError()) {
1479             QString typeName = scriptParser.referencedTypes().at(ii)->name;
1480
1481             QList<QDeclarativeError> errors = type.typeData->errors();
1482             QDeclarativeError error;
1483             error.setUrl(finalUrl());
1484             error.setLine(type.location.line);
1485             error.setColumn(type.location.column);
1486             error.setDescription(QDeclarativeTypeLoader::tr("Type %1 unavailable").arg(typeName));
1487             errors.prepend(error);
1488             setError(errors);
1489         }
1490     }
1491
1492     // Compile component
1493     if (!isError()) 
1494         compile();
1495
1496     if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
1497         scriptParser.clear();
1498 }
1499
1500 void QDeclarativeTypeData::completed()
1501 {
1502     // Notify callbacks
1503     while (!m_callbacks.isEmpty()) {
1504         TypeDataCallback *callback = m_callbacks.takeFirst();
1505         callback->typeDataReady(this);
1506     }
1507 }
1508
1509 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
1510 {
1511     if (!scriptParser.parse(data, finalUrl())) {
1512         setError(scriptParser.errors());
1513         return;
1514     }
1515
1516     m_imports.setBaseUrl(finalUrl());
1517
1518     foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
1519         if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) {
1520             QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1521             if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
1522                 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
1523                 addDependency(data);
1524                 m_qmldirs << data;
1525             }
1526         } else if (import.type == QDeclarativeScript::Import::Script) {
1527             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1528             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1529             addDependency(blob);
1530
1531             ScriptReference ref;
1532             ref.location = import.location.start;
1533             ref.qualifier = import.qualifier;
1534             ref.script = blob;
1535             m_scripts << ref;
1536
1537         }
1538     }
1539
1540     if (!finalUrl().scheme().isEmpty()) {
1541         QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
1542         if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
1543             QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
1544             addDependency(data);
1545             m_qmldirs << data;
1546         }
1547     }
1548 }
1549
1550 void QDeclarativeTypeData::allDependenciesDone()
1551 {
1552     if (!m_typesResolved) {
1553         resolveTypes();
1554         m_typesResolved = true;
1555     }
1556 }
1557
1558 void QDeclarativeTypeData::downloadProgressChanged(qreal p)
1559 {
1560     for (int ii = 0; ii < m_callbacks.count(); ++ii) {
1561         TypeDataCallback *callback = m_callbacks.at(ii);
1562         callback->typeDataProgress(this, p);
1563     }
1564 }
1565
1566 void QDeclarativeTypeData::compile()
1567 {
1568     Q_ASSERT(m_compiledData == 0);
1569     QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Compiling);
1570
1571     m_compiledData = new QDeclarativeCompiledData(typeLoader()->engine());
1572     m_compiledData->url = m_imports.baseUrl();
1573     m_compiledData->name = m_compiledData->url.toString();
1574     QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
1575
1576     QDeclarativeCompiler compiler(&scriptParser._pool);
1577     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
1578         setError(compiler.errors());
1579         m_compiledData->release();
1580         m_compiledData = 0;
1581     }
1582     QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Compiling);
1583 }
1584
1585 void QDeclarativeTypeData::resolveTypes()
1586 {
1587     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
1588     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
1589
1590     // For local urls, add an implicit import "." as first (most overridden) lookup. 
1591     // This will also trigger the loading of the qmldir and the import of any native 
1592     // types from available plugins.
1593     QList<QDeclarativeError> errors;
1594     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
1595         m_imports.addImport(importDatabase, QLatin1String("."),
1596                             QString(), -1, -1, QDeclarativeScript::Import::File, 
1597                             qmldir->dirComponents(), &errors);
1598     } else {
1599         m_imports.addImport(importDatabase, QLatin1String("."), 
1600                             QString(), -1, -1, QDeclarativeScript::Import::File, 
1601                             QDeclarativeDirComponents(), &errors);
1602     }
1603
1604     // remove any errors which are due to the implicit import which aren't real errors.
1605     // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
1606     QList<QDeclarativeError> realErrors;
1607     for (int i = 0; i < errors.size(); ++i) {
1608         if (errors.at(i).description() != QDeclarativeImportDatabase::tr("import \".\" has no qmldir and no namespace")
1609                 && errors.at(i).description() != QDeclarativeImportDatabase::tr("\".\": no such directory")) {
1610             realErrors.prepend(errors.at(i)); // this is a real error.
1611         }
1612     }
1613
1614     // report any real errors which occurred during plugin loading or qmldir parsing.
1615     if (!realErrors.isEmpty()) {
1616         setError(realErrors);
1617         return;
1618     }
1619
1620     foreach (const QDeclarativeScript::Import &import, scriptParser.imports()) {
1621         QDeclarativeDirComponents qmldircomponentsnetwork;
1622         if (import.type == QDeclarativeScript::Import::Script)
1623             continue;
1624
1625         if (import.type == QDeclarativeScript::Import::File && import.qualifier.isEmpty()) {
1626             QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
1627             if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
1628                 qmldircomponentsnetwork = qmldir->dirComponents();
1629         }
1630
1631         int vmaj = -1;
1632         int vmin = -1;
1633         import.extractVersion(&vmaj, &vmin);
1634
1635         QList<QDeclarativeError> errors;
1636         if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
1637                                  vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) {
1638             QDeclarativeError error;
1639             if (errors.size()) {
1640                 error = errors.takeFirst();
1641             } else {
1642                 // this should not be possible!
1643                 // Description should come from error provided by addImport() function.
1644                 error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1645             }
1646             error.setUrl(m_imports.baseUrl());
1647             error.setLine(import.location.start.line);
1648             error.setColumn(import.location.start.column);
1649             errors.prepend(error); // put it back on the list after filling out information.
1650
1651             setError(errors);
1652             return;
1653         }
1654     }
1655
1656     // Add any imported scripts to our resolved set
1657     foreach (const QDeclarativeImports::ScriptReference &script, m_imports.resolvedScripts())
1658     {
1659         QDeclarativeScriptBlob *blob = typeLoader()->getScript(script.location);
1660         addDependency(blob);
1661
1662         ScriptReference ref;
1663         //ref.location = ...
1664         ref.qualifier = script.nameSpace;
1665         if (!script.qualifier.isEmpty())
1666         {
1667             ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
1668
1669             // Add a reference to the enclosing namespace
1670             m_namespaces.insert(script.qualifier);
1671         }
1672
1673         ref.script = blob;
1674         m_scripts << ref;
1675     }
1676
1677     foreach (QDeclarativeScript::TypeReference *parserRef, scriptParser.referencedTypes()) {
1678         TypeReference ref;
1679
1680         QString url;
1681         int majorVersion;
1682         int minorVersion;
1683         QDeclarativeImportedNamespace *typeNamespace = 0;
1684         QList<QDeclarativeError> errors;
1685
1686         if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
1687                                    &typeNamespace, &errors) || typeNamespace) {
1688             // Known to not be a type:
1689             //  - known to be a namespace (Namespace {})
1690             //  - type with unknown namespace (UnknownNamespace.SomeType {})
1691             QDeclarativeError error;
1692             QString userTypeName = parserRef->name;
1693             userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
1694             if (typeNamespace) {
1695                 error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
1696             } else {
1697                 if (errors.size()) {
1698                     error = errors.takeFirst();
1699                 } else {
1700                     // this should not be possible!
1701                     // Description should come from error provided by addImport() function.
1702                     error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1703                 }
1704                 error.setUrl(m_imports.baseUrl());
1705                 error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description()));
1706             }
1707
1708             if (!parserRef->refObjects.isEmpty()) {
1709                 QDeclarativeScript::Object *obj = parserRef->refObjects.first();
1710                 error.setLine(obj->location.start.line);
1711                 error.setColumn(obj->location.start.column);
1712             }
1713
1714             errors.prepend(error);
1715             setError(errors);
1716             return;
1717         }
1718
1719         if (ref.type) {
1720             ref.majorVersion = majorVersion;
1721             ref.minorVersion = minorVersion;
1722         } else {
1723             ref.typeData = typeLoader()->get(QUrl(url));
1724             addDependency(ref.typeData);
1725         }
1726
1727         if (parserRef->refObjects.count())
1728             ref.location = parserRef->refObjects.first()->location.start;
1729
1730         m_types << ref;
1731     }
1732 }
1733
1734 QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
1735 {
1736     for (int ii = 0; ii < m_qmldirs.count(); ++ii) {
1737         if (m_qmldirs.at(ii)->url() == url)
1738             return m_qmldirs.at(ii);
1739     }
1740     return 0;
1741 }
1742
1743 QDeclarativeScriptData::QDeclarativeScriptData()
1744 : importCache(0), pragmas(QDeclarativeScript::Object::ScriptBlock::None), m_loaded(false) 
1745 {
1746 }
1747
1748 QDeclarativeScriptData::~QDeclarativeScriptData()
1749 {
1750 }
1751
1752 void QDeclarativeScriptData::clear()
1753 {
1754     if (importCache) {
1755         importCache->release();
1756         importCache = 0;
1757     }
1758
1759     for (int ii = 0; ii < scripts.count(); ++ii)
1760         scripts.at(ii)->release();
1761     scripts.clear();
1762
1763     qPersistentDispose(m_program);
1764     qPersistentDispose(m_value);
1765
1766     // An addref() was made when the QDeclarativeCleanup was added to the engine.
1767     release();
1768 }
1769
1770 QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader)
1771 : QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeScript::Object::ScriptBlock::None),
1772   m_imports(loader), m_scriptData(0), m_typeLoader(loader)
1773 {
1774 }
1775
1776 QDeclarativeScriptBlob::~QDeclarativeScriptBlob()
1777 {
1778     if (m_scriptData) {
1779         m_scriptData->release();
1780         m_scriptData = 0;
1781     }
1782 }
1783
1784 QDeclarativeScript::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
1785 {
1786     return m_pragmas;
1787 }
1788
1789 QString QDeclarativeScriptBlob::scriptSource() const
1790 {
1791     return m_source;
1792 }
1793
1794 QDeclarativeTypeLoader *QDeclarativeScriptBlob::typeLoader() const
1795 {
1796     return m_typeLoader;
1797 }
1798
1799 const QDeclarativeImports &QDeclarativeScriptBlob::imports() const
1800 {
1801     return m_imports;
1802 }
1803
1804 QDeclarativeScriptData *QDeclarativeScriptBlob::scriptData() const
1805 {
1806     return m_scriptData;
1807 }
1808
1809 void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
1810 {
1811     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
1812     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
1813
1814     m_source = QString::fromUtf8(data);
1815
1816     QDeclarativeScript::Parser::JavaScriptMetaData metadata =
1817         QDeclarativeScript::Parser::extractMetaData(m_source);
1818
1819     m_imports.setBaseUrl(finalUrl());
1820
1821     m_pragmas = metadata.pragmas;
1822
1823     foreach (const QDeclarativeScript::Import &import, metadata.imports) {
1824         Q_ASSERT(import.type != QDeclarativeScript::Import::File);
1825
1826         if (import.type == QDeclarativeScript::Import::Script) {
1827             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1828             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1829             addDependency(blob);
1830
1831             ScriptReference ref;
1832             ref.location = import.location.start;
1833             ref.qualifier = import.qualifier;
1834             ref.script = blob;
1835             m_scripts << ref;
1836         } else {
1837             Q_ASSERT(import.type == QDeclarativeScript::Import::Library);
1838             int vmaj = -1;
1839             int vmin = -1;
1840             import.extractVersion(&vmaj, &vmin);
1841
1842             QList<QDeclarativeError> errors;
1843             if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
1844                                      import.type, QDeclarativeDirComponents(), &errors)) {
1845                 QDeclarativeError error = errors.takeFirst();
1846                 // description should be set by addImport().
1847                 error.setUrl(m_imports.baseUrl());
1848                 error.setLine(import.location.start.line);
1849                 error.setColumn(import.location.start.column);
1850                 errors.prepend(error);
1851
1852                 setError(errors);
1853                 return;
1854             }
1855         }
1856     }
1857 }
1858
1859 void QDeclarativeScriptBlob::done()
1860 {
1861     // Check all script dependencies for errors
1862     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1863         const ScriptReference &script = m_scripts.at(ii);
1864         Q_ASSERT(script.script->isCompleteOrError());
1865         if (script.script->isError()) {
1866             QList<QDeclarativeError> errors = script.script->errors();
1867             QDeclarativeError error;
1868             error.setUrl(finalUrl());
1869             error.setLine(script.location.line);
1870             error.setColumn(script.location.column);
1871             error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
1872             errors.prepend(error);
1873             setError(errors);
1874         }
1875     }
1876
1877     if (isError())
1878         return;
1879
1880     QDeclarativeEngine *engine = typeLoader()->engine();
1881     m_scriptData = new QDeclarativeScriptData();
1882     m_scriptData->url = finalUrl();
1883     m_scriptData->importCache = new QDeclarativeTypeNameCache();
1884
1885     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1886         const ScriptReference &script = m_scripts.at(ii);
1887
1888         m_scriptData->scripts.append(script.script);
1889         m_scriptData->importCache->add(script.qualifier, ii);
1890     }
1891
1892     m_imports.populateCache(m_scriptData->importCache, engine);
1893
1894     m_scriptData->pragmas = m_pragmas;
1895     m_scriptData->m_programSource = m_source;
1896 }
1897
1898 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
1899 : QDeclarativeDataBlob(url, QmldirFile)
1900 {
1901 }
1902
1903 const QDeclarativeDirComponents &QDeclarativeQmldirData::dirComponents() const
1904 {
1905     return m_components;
1906 }
1907
1908 void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
1909 {
1910     QDeclarativeDirParser parser;
1911     parser.setSource(QString::fromUtf8(data));
1912     parser.parse();
1913     m_components = parser.components();
1914 }
1915
1916 QT_END_NAMESPACE
1917
1918 #include "qdeclarativetypeloader.moc"