Merge branch 'qtquick2' into v8
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativetypeloader.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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/qdeclarativecompiler_p.h>
46 #include <private/qdeclarativecomponent_p.h>
47 #include <private/qdeclarativeglobal_p.h>
48 #include <private/qdeclarativedebugtrace_p.h>
49
50 #include <QtDeclarative/qdeclarativecomponent.h>
51 #include <QtCore/qdebug.h>
52 #include <QtCore/qdir.h>
53 #include <QtCore/qfile.h>
54
55 QT_BEGIN_NAMESPACE
56
57 /*!
58 \class QDeclarativeDataBlob
59 \brief The QDeclarativeDataBlob encapsulates a data request that can be issued to a QDeclarativeDataLoader.
60 \internal
61
62 QDeclarativeDataBlob's are loaded by a QDeclarativeDataLoader.  The user creates the QDeclarativeDataBlob
63 and then calls QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() to load it.
64 The QDeclarativeDataLoader invokes callbacks on the QDeclarativeDataBlob as data becomes available.
65 */
66
67 /*!
68 \enum QDeclarativeDataBlob::Status
69
70 This enum describes the status of the data blob.
71
72 \list
73 \o Null The blob has not yet been loaded by a QDeclarativeDataLoader
74 \o Loading The blob is loading network data.  The QDeclarativeDataBlob::setData() callback has not yet been
75 invoked or has not yet returned.
76 \o WaitingForDependencies The blob is waiting for dependencies to be done before continueing.  This status
77 only occurs after the QDeclarativeDataBlob::setData() callback has been made, and when the blob has outstanding
78 dependencies.
79 \o Complete The blob's data has been loaded and all dependencies are done.
80 \o Error An error has been set on this blob.
81 \endlist
82 */
83
84 /*!
85 \enum QDeclarativeDataBlob::Type
86
87 This enum describes the type of the data blob.
88
89 \list
90 \o QmlFile This is a QDeclarativeTypeData
91 \o JavaScriptFile This is a QDeclarativeScriptData
92 \o QmldirFile This is a QDeclarativeQmldirData
93 \endlist
94 */
95
96 /*!
97 Create a new QDeclarativeDataBlob for \a url and of the provided \a type.
98 */
99 QDeclarativeDataBlob::QDeclarativeDataBlob(const QUrl &url, Type type)
100 : m_type(type), m_status(Null), m_progress(0), m_url(url), m_finalUrl(url), m_manager(0),
101   m_redirectCount(0), m_inCallback(false), m_isDone(false)
102 {
103 }
104
105 /*!  \internal */
106 QDeclarativeDataBlob::~QDeclarativeDataBlob()
107 {
108     Q_ASSERT(m_waitingOnMe.isEmpty());
109
110     cancelAllWaitingFor();
111 }
112
113 /*!
114 Returns the type provided to the constructor.
115 */
116 QDeclarativeDataBlob::Type QDeclarativeDataBlob::type() const
117 {
118     return m_type;
119 }
120
121 /*!
122 Returns the blob's status.
123 */
124 QDeclarativeDataBlob::Status QDeclarativeDataBlob::status() const
125 {
126     return m_status;
127 }
128
129 /*!
130 Returns true if the status is Null.
131 */
132 bool QDeclarativeDataBlob::isNull() const
133 {
134     return m_status == Null;
135 }
136
137 /*!
138 Returns true if the status is Loading.
139 */
140 bool QDeclarativeDataBlob::isLoading() const
141 {
142     return m_status == Loading;
143 }
144
145 /*!
146 Returns true if the status is WaitingForDependencies.
147 */
148 bool QDeclarativeDataBlob::isWaiting() const
149 {
150     return m_status == WaitingForDependencies;
151 }
152
153 /*!
154 Returns true if the status is Complete.
155 */
156 bool QDeclarativeDataBlob::isComplete() const
157 {
158     return m_status == Complete;
159 }
160
161 /*!
162 Returns true if the status is Error.
163 */
164 bool QDeclarativeDataBlob::isError() const
165 {
166     return m_status == Error;
167 }
168
169 /*!
170 Returns true if the status is Complete or Error.
171 */
172 bool QDeclarativeDataBlob::isCompleteOrError() const
173 {
174     return isComplete() || isError();
175 }
176
177 /*!
178 Returns the data download progress from 0 to 1.
179 */
180 qreal QDeclarativeDataBlob::progress() const
181 {
182     return m_progress;
183 }
184
185 /*!
186 Returns the blob url passed to the constructor.  If a network redirect
187 happens while fetching the data, this url remains the same.
188
189 \sa finalUrl()
190 */
191 QUrl QDeclarativeDataBlob::url() const
192 {
193     return m_url;
194 }
195
196 /*!
197 Returns the final url of the data.  Initially this is the same as
198 url(), but if a network redirect happens while fetching the data, this url
199 is updated to reflect the new location.
200 */
201 QUrl QDeclarativeDataBlob::finalUrl() const
202 {
203     return m_finalUrl;
204 }
205
206 /*!
207 Return the errors on this blob.
208 */
209 QList<QDeclarativeError> QDeclarativeDataBlob::errors() const
210 {
211     return m_errors;
212 }
213
214 /*!
215 Mark this blob as having \a errors.
216
217 All outstanding dependencies will be cancelled.  Requests to add new dependencies 
218 will be ignored.  Entry into the Error state is irreversable, although you can change the 
219 specific errors by additional calls to setError.
220 */
221 void QDeclarativeDataBlob::setError(const QDeclarativeError &errors)
222 {
223     QList<QDeclarativeError> l;
224     l << errors;
225     setError(l);
226 }
227
228 /*!
229 \overload
230 */
231 void QDeclarativeDataBlob::setError(const QList<QDeclarativeError> &errors)
232 {
233     m_status = Error;
234     m_errors = errors;
235
236     cancelAllWaitingFor();
237
238     if (!m_inCallback)
239         tryDone();
240 }
241
242 /*! 
243 Wait for \a blob to become complete or to error.  If \a blob is already 
244 complete or in error, or this blob is already complete, this has no effect.
245 */
246 void QDeclarativeDataBlob::addDependency(QDeclarativeDataBlob *blob)
247 {
248     Q_ASSERT(status() != Null);
249
250     if (!blob ||
251         blob->status() == Error || blob->status() == Complete ||
252         status() == Error || status() == Complete ||
253         m_waitingFor.contains(blob))
254         return;
255
256     blob->addref();
257     m_status = WaitingForDependencies;
258     m_waitingFor.append(blob);
259     blob->m_waitingOnMe.append(this);
260 }
261
262 /*!
263 \fn void QDeclarativeDataBlob::dataReceived(const QByteArray &data)
264
265 Invoked when data for the blob is received.  Implementors should use this callback
266 to determine a blob's dependencies.  Within this callback you may call setError()
267 or addDependency().
268 */
269
270 /*!
271 Invoked once data has either been received or a network error occurred, and all 
272 dependencies are complete.
273
274 You can set an error in this method, but you cannot add new dependencies.  Implementors
275 should use this callback to finalize processing of data.
276
277 The default implementation does nothing.
278 */
279 void QDeclarativeDataBlob::done()
280 {
281 }
282
283 /*!
284 Invoked if there is a network error while fetching this blob.
285
286 The default implementation sets an appropriate QDeclarativeError.
287 */
288 void QDeclarativeDataBlob::networkError(QNetworkReply::NetworkError networkError)
289 {
290     Q_UNUSED(networkError);
291
292     QDeclarativeError error;
293     error.setUrl(m_finalUrl);
294
295     const char *errorString = 0;
296     switch (networkError) {
297         default:
298             errorString = "Network error";
299             break;
300         case QNetworkReply::ConnectionRefusedError:
301             errorString = "Connection refused";
302             break;
303         case QNetworkReply::RemoteHostClosedError:
304             errorString = "Remote host closed the connection";
305             break;
306         case QNetworkReply::HostNotFoundError:
307             errorString = "Host not found";
308             break;
309         case QNetworkReply::TimeoutError:
310             errorString = "Timeout";
311             break;
312         case QNetworkReply::ProxyConnectionRefusedError:
313         case QNetworkReply::ProxyConnectionClosedError:
314         case QNetworkReply::ProxyNotFoundError:
315         case QNetworkReply::ProxyTimeoutError:
316         case QNetworkReply::ProxyAuthenticationRequiredError:
317         case QNetworkReply::UnknownProxyError:
318             errorString = "Proxy error";
319             break;
320         case QNetworkReply::ContentAccessDenied:
321             errorString = "Access denied";
322             break;
323         case QNetworkReply::ContentNotFoundError:
324             errorString = "File not found";
325             break;
326         case QNetworkReply::AuthenticationRequiredError:
327             errorString = "Authentication required";
328             break;
329     };
330
331     error.setDescription(QLatin1String(errorString));
332
333     setError(error);
334 }
335
336 /*! 
337 Called if \a blob, which was previously waited for, has an error.
338
339 The default implementation does nothing.
340 */
341 void QDeclarativeDataBlob::dependencyError(QDeclarativeDataBlob *blob)
342 {
343     Q_UNUSED(blob);
344 }
345
346 /*!
347 Called if \a blob, which was previously waited for, has completed.
348
349 The default implementation does nothing.
350 */
351 void QDeclarativeDataBlob::dependencyComplete(QDeclarativeDataBlob *blob)
352 {
353     Q_UNUSED(blob);
354 }
355
356 /*! 
357 Called when all blobs waited for have completed.  This occurs regardless of 
358 whether they are in error, or complete state.  
359
360 The default implementation does nothing.
361 */
362 void QDeclarativeDataBlob::allDependenciesDone()
363 {
364 }
365
366 /*!
367 Called when the download progress of this blob changes.  \a progress goes
368 from 0 to 1.
369 */
370 void QDeclarativeDataBlob::downloadProgressChanged(qreal progress)
371 {
372     Q_UNUSED(progress);
373 }
374
375 void QDeclarativeDataBlob::tryDone()
376 {
377     if (status() != Loading && m_waitingFor.isEmpty() && !m_isDone) {
378         if (status() != Error)
379             m_status = Complete;
380
381         m_isDone = true;
382         done();
383         notifyAllWaitingOnMe();
384     }
385 }
386
387 void QDeclarativeDataBlob::cancelAllWaitingFor()
388 {
389     while (m_waitingFor.count()) {
390         QDeclarativeDataBlob *blob = m_waitingFor.takeLast();
391
392         Q_ASSERT(blob->m_waitingOnMe.contains(this));
393
394         blob->m_waitingOnMe.removeOne(this);
395
396         blob->release();
397     }
398 }
399
400 void QDeclarativeDataBlob::notifyAllWaitingOnMe()
401 {
402     while (m_waitingOnMe.count()) {
403         QDeclarativeDataBlob *blob = m_waitingOnMe.takeLast();
404
405         Q_ASSERT(blob->m_waitingFor.contains(this));
406
407         blob->notifyComplete(this);
408     }
409 }
410
411 void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
412 {
413     Q_ASSERT(m_waitingFor.contains(blob));
414     Q_ASSERT(blob->status() == Error || blob->status() == Complete);
415
416     m_inCallback = true;
417
418     if (blob->status() == Error) {
419         dependencyError(blob);
420     } else if (blob->status() == Complete) {
421         dependencyComplete(blob);
422     }
423
424     m_waitingFor.removeOne(blob);
425     blob->release();
426
427     if (!isError() && m_waitingFor.isEmpty()) 
428         allDependenciesDone();
429
430     m_inCallback = false;
431
432     tryDone();
433 }
434
435 /*!
436 \class QDeclarativeDataLoader
437 \brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network.
438 \internal
439
440 The QDeclarativeDataLoader class is provided for the exclusive use of the QDeclarativeTypeLoader class.
441
442 Clients create QDeclarativeDataBlob instances and submit them to the QDeclarativeDataLoader class
443 through the QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() methods.
444 The loader then fetches the data over the network or from the local file system in an efficient way.
445 QDeclarativeDataBlob is an abstract class, so should always be specialized.
446
447 Once data is received, the QDeclarativeDataBlob::dataReceived() method is invoked on the blob.  The
448 derived class should use this callback to process the received data.  Processing of the data can 
449 result in an error being set (QDeclarativeDataBlob::setError()), or one or more dependencies being
450 created (QDeclarativeDataBlob::addDependency()).  Dependencies are other QDeclarativeDataBlob's that
451 are required before processing can fully complete.
452
453 To complete processing, the QDeclarativeDataBlob::done() callback is invoked.  done() is called when
454 one of these three preconditions are met.
455
456 \list 1
457 \o The QDeclarativeDataBlob has no dependencies.
458 \o The QDeclarativeDataBlob has an error set.
459 \o All the QDeclarativeDataBlob's dependencies are themselves "done()".
460 \endlist
461
462 Thus QDeclarativeDataBlob::done() will always eventually be called, even if the blob has an error set.
463 */
464
465 /*!
466 Create a new QDeclarativeDataLoader for \a engine.
467 */
468 QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
469 : m_engine(engine)
470 {
471 }
472
473 /*! \internal */
474 QDeclarativeDataLoader::~QDeclarativeDataLoader()
475 {
476     for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) 
477         (*iter)->release();
478 }
479
480 /*!
481 Load the provided \a blob from the network or filesystem.
482 */
483 void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
484 {
485     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
486     Q_ASSERT(blob->m_manager == 0);
487
488     blob->m_status = QDeclarativeDataBlob::Loading;
489
490     if (blob->m_url.isEmpty()) {
491         QDeclarativeError error;
492         error.setDescription(QLatin1String("Invalid null URL"));
493         blob->setError(error);
494         return;
495     }
496
497     QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
498
499     if (!lf.isEmpty()) {
500         if (!QDeclarative_isFileCaseCorrect(lf)) {
501             QDeclarativeError error;
502             error.setUrl(blob->m_url);
503             error.setDescription(QLatin1String("File name case mismatch"));
504             blob->setError(error);
505             return;
506         }
507         QFile file(lf);
508         if (file.open(QFile::ReadOnly)) {
509             QByteArray data = file.readAll();
510
511             blob->m_progress = 1.;
512             blob->downloadProgressChanged(1.);
513
514             setData(blob, data);
515         } else {
516             blob->networkError(QNetworkReply::ContentNotFoundError);
517         }
518
519     } else {
520
521         blob->m_manager = this;
522         QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(blob->m_url));
523         QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), 
524                          this, SLOT(networkReplyProgress(qint64,qint64)));
525         QObject::connect(reply, SIGNAL(finished()), 
526                          this, SLOT(networkReplyFinished()));
527         m_networkReplies.insert(reply, blob);
528
529         blob->addref();
530     }
531 }
532
533 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
534
535 void QDeclarativeDataLoader::networkReplyFinished()
536 {
537     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
538     reply->deleteLater();
539
540     QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
541
542     Q_ASSERT(blob);
543
544     blob->m_redirectCount++;
545
546     if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
547         QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
548         if (redirect.isValid()) {
549             QUrl url = reply->url().resolved(redirect.toUrl());
550             blob->m_finalUrl = url;
551
552             QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(url));
553             QObject::connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished()));
554             m_networkReplies.insert(reply, blob);
555             return;
556         }
557     }
558
559     if (reply->error()) {
560         blob->networkError(reply->error());
561     } else {
562         QByteArray data = reply->readAll();
563         setData(blob, data);
564     }
565
566     blob->release();
567 }
568
569 void QDeclarativeDataLoader::networkReplyProgress(qint64 bytesReceived, qint64 bytesTotal)
570 {
571     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
572     QDeclarativeDataBlob *blob = m_networkReplies.value(reply);
573
574     Q_ASSERT(blob);
575
576     if (bytesTotal != 0) {
577         blob->m_progress = bytesReceived / bytesTotal;
578         blob->downloadProgressChanged(blob->m_progress);
579     }
580 }
581
582 /*!
583 Load the provided \a blob with \a data.  The blob's URL is not used by the data loader in this case.
584 */
585 void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data)
586 {
587     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
588     Q_ASSERT(blob->m_manager == 0);
589     
590     blob->m_status = QDeclarativeDataBlob::Loading;
591
592     setData(blob, data);
593 }
594
595 /*!
596 Return the QDeclarativeEngine associated with this loader
597 */
598 QDeclarativeEngine *QDeclarativeDataLoader::engine() const
599 {
600     return m_engine;
601 }
602
603 void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
604 {
605     blob->m_inCallback = true;
606
607     blob->dataReceived(data);
608
609     if (!blob->isError() && !blob->isWaiting())
610         blob->allDependenciesDone();
611
612     if (blob->status() != QDeclarativeDataBlob::Error)
613         blob->m_status = QDeclarativeDataBlob::WaitingForDependencies;
614
615     blob->m_inCallback = false;
616
617     blob->tryDone();
618 }
619
620 /*!
621 Constructs a new type loader that uses the given \a engine.
622 */
623 QDeclarativeTypeLoader::QDeclarativeTypeLoader(QDeclarativeEngine *engine)
624 : QDeclarativeDataLoader(engine)
625 {
626 }
627
628 /*!
629 Destroys the type loader, first clearing the cache of any information about
630 loaded files.
631 */
632 QDeclarativeTypeLoader::~QDeclarativeTypeLoader()
633 {
634     clearCache();
635 }
636
637 /*!
638 \enum QDeclarativeTypeLoader::Option
639
640 This enum defines the options that control the way type data is handled.
641
642 \value None             The default value, indicating that no other options
643                         are enabled.
644 \value PreserveParser   The parser used to handle the type data is preserved
645                         after the data has been parsed.
646 */
647
648 /*!
649 Returns a QDeclarativeTypeData for the specified \a url.  The QDeclarativeTypeData may be cached.
650 */
651 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
652 {
653     Q_ASSERT(!url.isRelative() && 
654             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
655              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
656
657     QDeclarativeTypeData *typeData = m_typeCache.value(url);
658
659     if (!typeData) {
660         typeData = new QDeclarativeTypeData(url, None, this);
661         m_typeCache.insert(url, typeData);
662         QDeclarativeDataLoader::load(typeData);
663     }
664
665     typeData->addref();
666     return typeData;
667 }
668
669 /*!
670 Returns a QDeclarativeTypeData for the given \a data with the provided base \a url.  The 
671 QDeclarativeTypeData will not be cached.
672
673 The specified \a options control how the loader handles type data.
674 */
675 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
676 {
677     QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
678     QDeclarativeDataLoader::loadWithStaticData(typeData, data);
679     return typeData;
680 }
681
682 /*!
683 Return a QDeclarativeScriptBlob for \a url.  The QDeclarativeScriptData may be cached.
684 */
685 QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
686 {
687     Q_ASSERT(!url.isRelative() && 
688             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
689              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
690
691     QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
692
693     if (!scriptBlob) {
694         scriptBlob = new QDeclarativeScriptBlob(url, this);
695         m_scriptCache.insert(url, scriptBlob);
696         QDeclarativeDataLoader::load(scriptBlob);
697     }
698
699     return scriptBlob;
700 }
701
702 /*!
703 Returns a QDeclarativeQmldirData for \a url.  The QDeclarativeQmldirData may be cached.
704 */
705 QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
706 {
707     Q_ASSERT(!url.isRelative() && 
708             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
709              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
710
711     QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
712
713     if (!qmldirData) {
714         qmldirData = new QDeclarativeQmldirData(url);
715         m_qmldirCache.insert(url, qmldirData);
716         QDeclarativeDataLoader::load(qmldirData);
717     }
718
719     qmldirData->addref();
720     return qmldirData;
721 }
722
723 /*!
724 Clears cached information about loaded files, including any type data, scripts
725 and qmldir information.
726 */
727 void QDeclarativeTypeLoader::clearCache()
728 {
729     for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) 
730         (*iter)->release();
731     for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) 
732         (*iter)->release();
733     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) 
734         (*iter)->release();
735
736     m_typeCache.clear();
737     m_scriptCache.clear();
738     m_qmldirCache.clear();
739 }
740
741
742 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, 
743                                            QDeclarativeTypeLoader *manager)
744 : QDeclarativeDataBlob(url, QmlFile), m_options(options), m_typesResolved(false), 
745   m_compiledData(0), m_typeLoader(manager)
746 {
747 }
748
749 QDeclarativeTypeData::~QDeclarativeTypeData()
750 {
751     for (int ii = 0; ii < m_scripts.count(); ++ii) 
752         m_scripts.at(ii).script->release();
753     for (int ii = 0; ii < m_qmldirs.count(); ++ii) 
754         m_qmldirs.at(ii)->release();
755     for (int ii = 0; ii < m_types.count(); ++ii) 
756         if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
757     if (m_compiledData)
758         m_compiledData->release();
759 }
760
761 QDeclarativeTypeLoader *QDeclarativeTypeData::typeLoader() const
762 {
763     return m_typeLoader;
764 }
765
766 const QDeclarativeImports &QDeclarativeTypeData::imports() const
767 {
768     return m_imports;
769 }
770
771 const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const
772 {
773     return scriptParser;
774 }
775
776 const QList<QDeclarativeTypeData::TypeReference> &QDeclarativeTypeData::resolvedTypes() const
777 {
778     return m_types;
779 }
780
781 const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolvedScripts() const
782 {
783     return m_scripts;
784 }
785
786 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
787 {
788     if (m_compiledData) 
789         m_compiledData->addref();
790
791     return m_compiledData;
792 }
793
794 void QDeclarativeTypeData::registerCallback(TypeDataCallback *callback)
795 {
796     Q_ASSERT(!m_callbacks.contains(callback));
797     m_callbacks.append(callback);
798 }
799
800 void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
801 {
802     Q_ASSERT(m_callbacks.contains(callback));
803     m_callbacks.removeOne(callback);
804     Q_ASSERT(!m_callbacks.contains(callback));
805 }
806
807 void QDeclarativeTypeData::done()
808 {
809     addref();
810
811     // Check all script dependencies for errors
812     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
813         const ScriptReference &script = m_scripts.at(ii);
814         Q_ASSERT(script.script->isCompleteOrError());
815         if (script.script->isError()) {
816             QList<QDeclarativeError> errors = script.script->errors();
817             QDeclarativeError error;
818             error.setUrl(finalUrl());
819             error.setLine(script.location.line);
820             error.setColumn(script.location.column);
821             error.setDescription(QDeclarativeTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
822             errors.prepend(error);
823             setError(errors);
824         }
825     }
826
827     // Check all type dependencies for errors
828     for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
829         const TypeReference &type = m_types.at(ii);
830         Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
831         if (type.typeData && type.typeData->isError()) {
832             QString typeName = scriptParser.referencedTypes().at(ii)->name;
833
834             QList<QDeclarativeError> errors = type.typeData->errors();
835             QDeclarativeError error;
836             error.setUrl(finalUrl());
837             error.setLine(type.location.line);
838             error.setColumn(type.location.column);
839             error.setDescription(QDeclarativeTypeLoader::tr("Type %1 unavailable").arg(typeName));
840             errors.prepend(error);
841             setError(errors);
842         }
843     }
844
845     // Compile component
846     if (!isError()) 
847         compile();
848
849     if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
850         scriptParser.clear();
851
852     // Notify callbacks
853     while (!m_callbacks.isEmpty()) {
854         TypeDataCallback *callback = m_callbacks.takeFirst();
855         callback->typeDataReady(this);
856     }
857
858     release();
859 }
860
861 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
862 {
863     if (!scriptParser.parse(data, finalUrl())) {
864         setError(scriptParser.errors());
865         return;
866     }
867
868     m_imports.setBaseUrl(finalUrl());
869
870     foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
871         if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
872             QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
873             if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
874                 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
875                 addDependency(data);
876                 m_qmldirs << data;
877             }
878         } else if (import.type == QDeclarativeScriptParser::Import::Script) {
879             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
880             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
881             addDependency(blob);
882
883             ScriptReference ref;
884             ref.location = import.location.start;
885             ref.qualifier = import.qualifier;
886             ref.script = blob;
887             blob->addref();
888             m_scripts << ref;
889
890         }
891     }
892
893     if (!finalUrl().scheme().isEmpty()) {
894         QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
895         if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
896             QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
897             addDependency(data);
898             m_qmldirs << data;
899         }
900     }
901 }
902
903 void QDeclarativeTypeData::allDependenciesDone()
904 {
905     if (!m_typesResolved) {
906         resolveTypes();
907         m_typesResolved = true;
908     }
909 }
910
911 void QDeclarativeTypeData::downloadProgressChanged(qreal p)
912 {
913     for (int ii = 0; ii < m_callbacks.count(); ++ii) {
914         TypeDataCallback *callback = m_callbacks.at(ii);
915         callback->typeDataProgress(this, p);
916     }
917 }
918
919 void QDeclarativeTypeData::compile()
920 {
921     Q_ASSERT(m_compiledData == 0);
922     QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Compiling);
923
924     m_compiledData = new QDeclarativeCompiledData(typeLoader()->engine());
925     m_compiledData->url = m_imports.baseUrl();
926     m_compiledData->name = m_compiledData->url.toString();
927     QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
928
929     QDeclarativeCompiler compiler;
930     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
931         setError(compiler.errors());
932         m_compiledData->release();
933         m_compiledData = 0;
934     }
935     QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Compiling);
936 }
937
938 void QDeclarativeTypeData::resolveTypes()
939 {
940     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
941     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
942
943     // For local urls, add an implicit import "." as first (most overridden) lookup. 
944     // This will also trigger the loading of the qmldir and the import of any native 
945     // types from available plugins.
946     QList<QDeclarativeError> errors;
947     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
948         m_imports.addImport(importDatabase, QLatin1String("."),
949                             QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
950                             qmldir->dirComponents(), &errors);
951     } else {
952         m_imports.addImport(importDatabase, QLatin1String("."), 
953                             QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
954                             QDeclarativeDirComponents(), &errors);
955     }
956
957     // remove any errors which are due to the implicit import which aren't real errors.
958     // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
959     QList<QDeclarativeError> realErrors;
960     for (int i = 0; i < errors.size(); ++i) {
961         if (errors.at(i).description() != QDeclarativeImportDatabase::tr("import \".\" has no qmldir and no namespace")
962                 && errors.at(i).description() != QDeclarativeImportDatabase::tr("\".\": no such directory")) {
963             realErrors.prepend(errors.at(i)); // this is a real error.
964         }
965     }
966
967     // report any real errors which occurred during plugin loading or qmldir parsing.
968     if (!realErrors.isEmpty()) {
969         setError(realErrors);
970         return;
971     }
972
973     foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
974         QDeclarativeDirComponents qmldircomponentsnetwork;
975         if (import.type == QDeclarativeScriptParser::Import::Script)
976             continue;
977
978         if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
979             QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
980             if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
981                 qmldircomponentsnetwork = qmldir->dirComponents();
982         }
983
984         int vmaj = -1;
985         int vmin = -1;
986         import.extractVersion(&vmaj, &vmin);
987
988         QList<QDeclarativeError> errors;
989         if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
990                                  vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) {
991             QDeclarativeError error;
992             if (errors.size()) {
993                 error = errors.takeFirst();
994             } else {
995                 // this should not be possible!
996                 // Description should come from error provided by addImport() function.
997                 error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
998             }
999             error.setUrl(m_imports.baseUrl());
1000             error.setLine(import.location.start.line);
1001             error.setColumn(import.location.start.column);
1002             errors.prepend(error); // put it back on the list after filling out information.
1003
1004             setError(errors);
1005             return;
1006         }
1007     }
1008
1009     foreach (QDeclarativeScriptParser::TypeReference *parserRef, scriptParser.referencedTypes()) {
1010         QByteArray typeName = parserRef->name.toUtf8();
1011
1012         TypeReference ref;
1013
1014         QUrl url;
1015         int majorVersion;
1016         int minorVersion;
1017         QDeclarativeImportedNamespace *typeNamespace = 0;
1018         QList<QDeclarativeError> errors;
1019
1020         if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion,
1021                                    &typeNamespace, &errors) || typeNamespace) {
1022             // Known to not be a type:
1023             //  - known to be a namespace (Namespace {})
1024             //  - type with unknown namespace (UnknownNamespace.SomeType {})
1025             QDeclarativeError error;
1026             QString userTypeName = parserRef->name;
1027             userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
1028             if (typeNamespace) {
1029                 error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
1030             } else {
1031                 if (errors.size()) {
1032                     error = errors.takeFirst();
1033                 } else {
1034                     // this should not be possible!
1035                     // Description should come from error provided by addImport() function.
1036                     error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1037                 }
1038                 error.setUrl(m_imports.baseUrl());
1039                 error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description()));
1040             }
1041
1042             if (!parserRef->refObjects.isEmpty()) {
1043                 QDeclarativeParser::Object *obj = parserRef->refObjects.first();
1044                 error.setLine(obj->location.start.line);
1045                 error.setColumn(obj->location.start.column);
1046             }
1047
1048             errors.prepend(error);
1049             setError(errors);
1050             return;
1051         }
1052
1053         if (ref.type) {
1054             ref.majorVersion = majorVersion;
1055             ref.minorVersion = minorVersion;
1056             foreach (QDeclarativeParser::Object *obj, parserRef->refObjects) {
1057                // store namespace for DOM
1058                obj->majorVersion = majorVersion;
1059                obj->minorVersion = minorVersion;
1060             }
1061         } else {
1062             ref.typeData = typeLoader()->get(url);
1063             addDependency(ref.typeData);
1064         }
1065
1066         if (parserRef->refObjects.count())
1067             ref.location = parserRef->refObjects.first()->location.start;
1068
1069         m_types << ref;
1070     }
1071 }
1072
1073 QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
1074 {
1075     for (int ii = 0; ii < m_qmldirs.count(); ++ii) {
1076         if (m_qmldirs.at(ii)->url() == url)
1077             return m_qmldirs.at(ii);
1078     }
1079     return 0;
1080 }
1081
1082 QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine)
1083 : QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeParser::Object::ScriptBlock::None),
1084   m_loaded(false)
1085 {
1086 }
1087
1088 QDeclarativeScriptData::~QDeclarativeScriptData()
1089 {
1090     clear();
1091 }
1092
1093 void QDeclarativeScriptData::clear()
1094 {
1095     if (importCache) {
1096         importCache->release();
1097         importCache = 0;
1098     }
1099
1100     for (int ii = 0; ii < scripts.count(); ++ii)
1101         scripts.at(ii)->release();
1102     scripts.clear();
1103
1104     qPersistentDispose(m_program);
1105     qPersistentDispose(m_value);
1106 }
1107
1108 QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader)
1109 : QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None),
1110   m_scriptData(0), m_typeLoader(loader)
1111 {
1112 }
1113
1114 QDeclarativeScriptBlob::~QDeclarativeScriptBlob()
1115 {
1116     if (m_scriptData) {
1117         m_scriptData->release();
1118         m_scriptData = 0;
1119     }
1120 }
1121
1122 QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
1123 {
1124     return m_pragmas;
1125 }
1126
1127 QString QDeclarativeScriptBlob::scriptSource() const
1128 {
1129     return m_source;
1130 }
1131
1132 QDeclarativeTypeLoader *QDeclarativeScriptBlob::typeLoader() const
1133 {
1134     return m_typeLoader;
1135 }
1136
1137 const QDeclarativeImports &QDeclarativeScriptBlob::imports() const
1138 {
1139     return m_imports;
1140 }
1141
1142 QDeclarativeScriptData *QDeclarativeScriptBlob::scriptData() const
1143 {
1144     return m_scriptData;
1145 }
1146
1147 void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
1148 {
1149     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
1150     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
1151
1152     m_source = QString::fromUtf8(data);
1153
1154     QDeclarativeScriptParser::JavaScriptMetaData metadata =
1155         QDeclarativeScriptParser::extractMetaData(m_source);
1156
1157     m_imports.setBaseUrl(finalUrl());
1158
1159     m_pragmas = metadata.pragmas;
1160
1161     foreach (const QDeclarativeScriptParser::Import &import, metadata.imports) {
1162         Q_ASSERT(import.type != QDeclarativeScriptParser::Import::File);
1163
1164         if (import.type == QDeclarativeScriptParser::Import::Script) {
1165             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1166             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1167             addDependency(blob);
1168
1169             ScriptReference ref;
1170             ref.location = import.location.start;
1171             ref.qualifier = import.qualifier;
1172             ref.script = blob;
1173             blob->addref();
1174             m_scripts << ref;
1175         } else {
1176             Q_ASSERT(import.type == QDeclarativeScriptParser::Import::Library);
1177             int vmaj = -1;
1178             int vmin = -1;
1179             import.extractVersion(&vmaj, &vmin);
1180
1181             QList<QDeclarativeError> errors;
1182             if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
1183                                      import.type, QDeclarativeDirComponents(), &errors)) {
1184                 QDeclarativeError error = errors.takeFirst();
1185                 // description should be set by addImport().
1186                 error.setUrl(m_imports.baseUrl());
1187                 error.setLine(import.location.start.line);
1188                 error.setColumn(import.location.start.column);
1189                 errors.prepend(error);
1190
1191                 setError(errors);
1192                 return;
1193             }
1194         }
1195     }
1196 }
1197
1198 void QDeclarativeScriptBlob::done()
1199 {
1200     // Check all script dependencies for errors
1201     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1202         const ScriptReference &script = m_scripts.at(ii);
1203         Q_ASSERT(script.script->isCompleteOrError());
1204         if (script.script->isError()) {
1205             QList<QDeclarativeError> errors = script.script->errors();
1206             QDeclarativeError error;
1207             error.setUrl(finalUrl());
1208             error.setLine(script.location.line);
1209             error.setColumn(script.location.column);
1210             error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
1211             errors.prepend(error);
1212             setError(errors);
1213         }
1214     }
1215
1216     if (isError())
1217         return;
1218
1219     QDeclarativeEngine *engine = typeLoader()->engine();
1220     m_scriptData = new QDeclarativeScriptData(engine);
1221     m_scriptData->url = finalUrl();
1222     m_scriptData->importCache = new QDeclarativeTypeNameCache(engine);
1223
1224     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1225         const ScriptReference &script = m_scripts.at(ii);
1226
1227         m_scriptData->scripts.append(script.script);
1228         m_scriptData->importCache->add(script.qualifier, ii);
1229     }
1230
1231     m_imports.populateCache(m_scriptData->importCache, engine);
1232
1233     m_scriptData->pragmas = m_pragmas;
1234
1235     // XXX TODO: Handle errors that occur duing the script compile
1236     QV8Engine *v8engine = &QDeclarativeEnginePrivate::get(engine)->v8engine;
1237     v8::HandleScope handle_scope;
1238     v8::Context::Scope scope(v8engine->context());
1239     v8::Local<v8::Script> program = v8engine->qmlModeCompile(m_source, finalUrl().toString(), 1);
1240     m_scriptData->m_program = qPersistentNew<v8::Script>(program);
1241 }
1242
1243 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
1244 : QDeclarativeDataBlob(url, QmldirFile)
1245 {
1246 }
1247
1248 const QDeclarativeDirComponents &QDeclarativeQmldirData::dirComponents() const
1249 {
1250     return m_components;
1251 }
1252
1253 void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
1254 {
1255     QDeclarativeDirParser parser;
1256     parser.setSource(QString::fromUtf8(data));
1257     parser.parse();
1258     m_components = parser.components();
1259 }
1260
1261 QT_END_NAMESPACE
1262