Improve QJSValueIterator implementation.
[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         addref();
383         done();
384         notifyAllWaitingOnMe();
385         release();
386     }
387 }
388
389 void QDeclarativeDataBlob::cancelAllWaitingFor()
390 {
391     while (m_waitingFor.count()) {
392         QDeclarativeDataBlob *blob = m_waitingFor.takeLast();
393
394         Q_ASSERT(blob->m_waitingOnMe.contains(this));
395
396         blob->m_waitingOnMe.removeOne(this);
397
398         blob->release();
399     }
400 }
401
402 void QDeclarativeDataBlob::notifyAllWaitingOnMe()
403 {
404     while (m_waitingOnMe.count()) {
405         QDeclarativeDataBlob *blob = m_waitingOnMe.takeLast();
406
407         Q_ASSERT(blob->m_waitingFor.contains(this));
408
409         blob->notifyComplete(this);
410     }
411 }
412
413 void QDeclarativeDataBlob::notifyComplete(QDeclarativeDataBlob *blob)
414 {
415     Q_ASSERT(m_waitingFor.contains(blob));
416     Q_ASSERT(blob->status() == Error || blob->status() == Complete);
417
418     m_inCallback = true;
419
420     if (blob->status() == Error) {
421         dependencyError(blob);
422     } else if (blob->status() == Complete) {
423         dependencyComplete(blob);
424     }
425
426     m_waitingFor.removeOne(blob);
427     blob->release();
428
429     if (!isError() && m_waitingFor.isEmpty()) 
430         allDependenciesDone();
431
432     m_inCallback = false;
433
434     tryDone();
435 }
436
437 /*!
438 \class QDeclarativeDataLoader
439 \brief The QDeclarativeDataLoader class abstracts loading files and their dependencies over the network.
440 \internal
441
442 The QDeclarativeDataLoader class is provided for the exclusive use of the QDeclarativeTypeLoader class.
443
444 Clients create QDeclarativeDataBlob instances and submit them to the QDeclarativeDataLoader class
445 through the QDeclarativeDataLoader::load() or QDeclarativeDataLoader::loadWithStaticData() methods.
446 The loader then fetches the data over the network or from the local file system in an efficient way.
447 QDeclarativeDataBlob is an abstract class, so should always be specialized.
448
449 Once data is received, the QDeclarativeDataBlob::dataReceived() method is invoked on the blob.  The
450 derived class should use this callback to process the received data.  Processing of the data can 
451 result in an error being set (QDeclarativeDataBlob::setError()), or one or more dependencies being
452 created (QDeclarativeDataBlob::addDependency()).  Dependencies are other QDeclarativeDataBlob's that
453 are required before processing can fully complete.
454
455 To complete processing, the QDeclarativeDataBlob::done() callback is invoked.  done() is called when
456 one of these three preconditions are met.
457
458 \list 1
459 \o The QDeclarativeDataBlob has no dependencies.
460 \o The QDeclarativeDataBlob has an error set.
461 \o All the QDeclarativeDataBlob's dependencies are themselves "done()".
462 \endlist
463
464 Thus QDeclarativeDataBlob::done() will always eventually be called, even if the blob has an error set.
465 */
466
467 /*!
468 Create a new QDeclarativeDataLoader for \a engine.
469 */
470 QDeclarativeDataLoader::QDeclarativeDataLoader(QDeclarativeEngine *engine)
471 : m_engine(engine)
472 {
473 }
474
475 /*! \internal */
476 QDeclarativeDataLoader::~QDeclarativeDataLoader()
477 {
478     for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) 
479         (*iter)->release();
480 }
481
482 /*!
483 Load the provided \a blob from the network or filesystem.
484 */
485 void QDeclarativeDataLoader::load(QDeclarativeDataBlob *blob)
486 {
487     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
488     Q_ASSERT(blob->m_manager == 0);
489
490     blob->m_status = QDeclarativeDataBlob::Loading;
491
492     if (blob->m_url.isEmpty()) {
493         QDeclarativeError error;
494         error.setDescription(QLatin1String("Invalid null URL"));
495         blob->setError(error);
496         return;
497     }
498
499     QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
500
501     if (!lf.isEmpty()) {
502         if (!QDeclarative_isFileCaseCorrect(lf)) {
503             QDeclarativeError error;
504             error.setUrl(blob->m_url);
505             error.setDescription(QLatin1String("File name case mismatch"));
506             blob->setError(error);
507             return;
508         }
509         QFile file(lf);
510         if (file.open(QFile::ReadOnly)) {
511             QByteArray data = file.readAll();
512
513             blob->m_progress = 1.;
514             blob->downloadProgressChanged(1.);
515
516             setData(blob, data);
517         } else {
518             blob->networkError(QNetworkReply::ContentNotFoundError);
519         }
520
521     } else {
522
523         blob->m_manager = this;
524         QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(blob->m_url));
525         QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), 
526                          this, SLOT(networkReplyProgress(qint64,qint64)));
527         QObject::connect(reply, SIGNAL(finished()), 
528                          this, SLOT(networkReplyFinished()));
529         m_networkReplies.insert(reply, blob);
530
531         blob->addref();
532     }
533 }
534
535 #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
536
537 void QDeclarativeDataLoader::networkReplyFinished()
538 {
539     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
540     reply->deleteLater();
541
542     QDeclarativeDataBlob *blob = m_networkReplies.take(reply);
543
544     Q_ASSERT(blob);
545
546     blob->m_redirectCount++;
547
548     if (blob->m_redirectCount < DATALOADER_MAXIMUM_REDIRECT_RECURSION) {
549         QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
550         if (redirect.isValid()) {
551             QUrl url = reply->url().resolved(redirect.toUrl());
552             blob->m_finalUrl = url;
553
554             QNetworkReply *reply = m_engine->networkAccessManager()->get(QNetworkRequest(url));
555             QObject::connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished()));
556             m_networkReplies.insert(reply, blob);
557             return;
558         }
559     }
560
561     if (reply->error()) {
562         blob->networkError(reply->error());
563     } else {
564         QByteArray data = reply->readAll();
565         setData(blob, data);
566     }
567
568     blob->release();
569 }
570
571 void QDeclarativeDataLoader::networkReplyProgress(qint64 bytesReceived, qint64 bytesTotal)
572 {
573     QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
574     QDeclarativeDataBlob *blob = m_networkReplies.value(reply);
575
576     Q_ASSERT(blob);
577
578     if (bytesTotal != 0) {
579         blob->m_progress = bytesReceived / bytesTotal;
580         blob->downloadProgressChanged(blob->m_progress);
581     }
582 }
583
584 /*!
585 Load the provided \a blob with \a data.  The blob's URL is not used by the data loader in this case.
586 */
587 void QDeclarativeDataLoader::loadWithStaticData(QDeclarativeDataBlob *blob, const QByteArray &data)
588 {
589     Q_ASSERT(blob->status() == QDeclarativeDataBlob::Null);
590     Q_ASSERT(blob->m_manager == 0);
591     
592     blob->m_status = QDeclarativeDataBlob::Loading;
593
594     setData(blob, data);
595 }
596
597 /*!
598 Return the QDeclarativeEngine associated with this loader
599 */
600 QDeclarativeEngine *QDeclarativeDataLoader::engine() const
601 {
602     return m_engine;
603 }
604
605 void QDeclarativeDataLoader::setData(QDeclarativeDataBlob *blob, const QByteArray &data)
606 {
607     blob->m_inCallback = true;
608
609     blob->dataReceived(data);
610
611     if (!blob->isError() && !blob->isWaiting())
612         blob->allDependenciesDone();
613
614     if (blob->status() != QDeclarativeDataBlob::Error)
615         blob->m_status = QDeclarativeDataBlob::WaitingForDependencies;
616
617     blob->m_inCallback = false;
618
619     blob->tryDone();
620 }
621
622 /*!
623 Constructs a new type loader that uses the given \a engine.
624 */
625 QDeclarativeTypeLoader::QDeclarativeTypeLoader(QDeclarativeEngine *engine)
626 : QDeclarativeDataLoader(engine)
627 {
628 }
629
630 /*!
631 Destroys the type loader, first clearing the cache of any information about
632 loaded files.
633 */
634 QDeclarativeTypeLoader::~QDeclarativeTypeLoader()
635 {
636     clearCache();
637 }
638
639 /*!
640 \enum QDeclarativeTypeLoader::Option
641
642 This enum defines the options that control the way type data is handled.
643
644 \value None             The default value, indicating that no other options
645                         are enabled.
646 \value PreserveParser   The parser used to handle the type data is preserved
647                         after the data has been parsed.
648 */
649
650 /*!
651 Returns a QDeclarativeTypeData for the specified \a url.  The QDeclarativeTypeData may be cached.
652 */
653 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QUrl &url)
654 {
655     Q_ASSERT(!url.isRelative() && 
656             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
657              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
658
659     QDeclarativeTypeData *typeData = m_typeCache.value(url);
660
661     if (!typeData) {
662         typeData = new QDeclarativeTypeData(url, None, this);
663         m_typeCache.insert(url, typeData);
664         QDeclarativeDataLoader::load(typeData);
665     }
666
667     typeData->addref();
668     return typeData;
669 }
670
671 /*!
672 Returns a QDeclarativeTypeData for the given \a data with the provided base \a url.  The 
673 QDeclarativeTypeData will not be cached.
674
675 The specified \a options control how the loader handles type data.
676 */
677 QDeclarativeTypeData *QDeclarativeTypeLoader::get(const QByteArray &data, const QUrl &url, Options options)
678 {
679     QDeclarativeTypeData *typeData = new QDeclarativeTypeData(url, options, this);
680     QDeclarativeDataLoader::loadWithStaticData(typeData, data);
681     return typeData;
682 }
683
684 /*!
685 Return a QDeclarativeScriptBlob for \a url.  The QDeclarativeScriptData may be cached.
686 */
687 QDeclarativeScriptBlob *QDeclarativeTypeLoader::getScript(const QUrl &url)
688 {
689     Q_ASSERT(!url.isRelative() && 
690             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
691              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
692
693     QDeclarativeScriptBlob *scriptBlob = m_scriptCache.value(url);
694
695     if (!scriptBlob) {
696         scriptBlob = new QDeclarativeScriptBlob(url, this);
697         m_scriptCache.insert(url, scriptBlob);
698         QDeclarativeDataLoader::load(scriptBlob);
699     }
700
701     return scriptBlob;
702 }
703
704 /*!
705 Returns a QDeclarativeQmldirData for \a url.  The QDeclarativeQmldirData may be cached.
706 */
707 QDeclarativeQmldirData *QDeclarativeTypeLoader::getQmldir(const QUrl &url)
708 {
709     Q_ASSERT(!url.isRelative() && 
710             (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() || 
711              !QDir::isRelativePath(QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url))));
712
713     QDeclarativeQmldirData *qmldirData = m_qmldirCache.value(url);
714
715     if (!qmldirData) {
716         qmldirData = new QDeclarativeQmldirData(url);
717         m_qmldirCache.insert(url, qmldirData);
718         QDeclarativeDataLoader::load(qmldirData);
719     }
720
721     qmldirData->addref();
722     return qmldirData;
723 }
724
725 /*!
726 Clears cached information about loaded files, including any type data, scripts
727 and qmldir information.
728 */
729 void QDeclarativeTypeLoader::clearCache()
730 {
731     for (TypeCache::Iterator iter = m_typeCache.begin(); iter != m_typeCache.end(); ++iter) 
732         (*iter)->release();
733     for (ScriptCache::Iterator iter = m_scriptCache.begin(); iter != m_scriptCache.end(); ++iter) 
734         (*iter)->release();
735     for (QmldirCache::Iterator iter = m_qmldirCache.begin(); iter != m_qmldirCache.end(); ++iter) 
736         (*iter)->release();
737
738     m_typeCache.clear();
739     m_scriptCache.clear();
740     m_qmldirCache.clear();
741 }
742
743
744 QDeclarativeTypeData::QDeclarativeTypeData(const QUrl &url, QDeclarativeTypeLoader::Options options, 
745                                            QDeclarativeTypeLoader *manager)
746 : QDeclarativeDataBlob(url, QmlFile), m_options(options), m_typesResolved(false), 
747   m_compiledData(0), m_typeLoader(manager)
748 {
749 }
750
751 QDeclarativeTypeData::~QDeclarativeTypeData()
752 {
753     for (int ii = 0; ii < m_scripts.count(); ++ii) 
754         m_scripts.at(ii).script->release();
755     for (int ii = 0; ii < m_qmldirs.count(); ++ii) 
756         m_qmldirs.at(ii)->release();
757     for (int ii = 0; ii < m_types.count(); ++ii) 
758         if (m_types.at(ii).typeData) m_types.at(ii).typeData->release();
759     if (m_compiledData)
760         m_compiledData->release();
761 }
762
763 QDeclarativeTypeLoader *QDeclarativeTypeData::typeLoader() const
764 {
765     return m_typeLoader;
766 }
767
768 const QDeclarativeImports &QDeclarativeTypeData::imports() const
769 {
770     return m_imports;
771 }
772
773 const QDeclarativeScriptParser &QDeclarativeTypeData::parser() const
774 {
775     return scriptParser;
776 }
777
778 const QList<QDeclarativeTypeData::TypeReference> &QDeclarativeTypeData::resolvedTypes() const
779 {
780     return m_types;
781 }
782
783 const QList<QDeclarativeTypeData::ScriptReference> &QDeclarativeTypeData::resolvedScripts() const
784 {
785     return m_scripts;
786 }
787
788 QDeclarativeCompiledData *QDeclarativeTypeData::compiledData() const
789 {
790     if (m_compiledData) 
791         m_compiledData->addref();
792
793     return m_compiledData;
794 }
795
796 void QDeclarativeTypeData::registerCallback(TypeDataCallback *callback)
797 {
798     Q_ASSERT(!m_callbacks.contains(callback));
799     m_callbacks.append(callback);
800 }
801
802 void QDeclarativeTypeData::unregisterCallback(TypeDataCallback *callback)
803 {
804     Q_ASSERT(m_callbacks.contains(callback));
805     m_callbacks.removeOne(callback);
806     Q_ASSERT(!m_callbacks.contains(callback));
807 }
808
809 void QDeclarativeTypeData::done()
810 {
811     addref();
812
813     // Check all script dependencies for errors
814     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
815         const ScriptReference &script = m_scripts.at(ii);
816         Q_ASSERT(script.script->isCompleteOrError());
817         if (script.script->isError()) {
818             QList<QDeclarativeError> errors = script.script->errors();
819             QDeclarativeError error;
820             error.setUrl(finalUrl());
821             error.setLine(script.location.line);
822             error.setColumn(script.location.column);
823             error.setDescription(QDeclarativeTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
824             errors.prepend(error);
825             setError(errors);
826         }
827     }
828
829     // Check all type dependencies for errors
830     for (int ii = 0; !isError() && ii < m_types.count(); ++ii) {
831         const TypeReference &type = m_types.at(ii);
832         Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
833         if (type.typeData && type.typeData->isError()) {
834             QString typeName = scriptParser.referencedTypes().at(ii)->name;
835
836             QList<QDeclarativeError> errors = type.typeData->errors();
837             QDeclarativeError error;
838             error.setUrl(finalUrl());
839             error.setLine(type.location.line);
840             error.setColumn(type.location.column);
841             error.setDescription(QDeclarativeTypeLoader::tr("Type %1 unavailable").arg(typeName));
842             errors.prepend(error);
843             setError(errors);
844         }
845     }
846
847     // Compile component
848     if (!isError()) 
849         compile();
850
851     if (!(m_options & QDeclarativeTypeLoader::PreserveParser))
852         scriptParser.clear();
853
854     // Notify callbacks
855     while (!m_callbacks.isEmpty()) {
856         TypeDataCallback *callback = m_callbacks.takeFirst();
857         callback->typeDataReady(this);
858     }
859
860     release();
861 }
862
863 void QDeclarativeTypeData::dataReceived(const QByteArray &data)
864 {
865     if (!scriptParser.parse(data, finalUrl())) {
866         setError(scriptParser.errors());
867         return;
868     }
869
870     m_imports.setBaseUrl(finalUrl());
871
872     foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
873         if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
874             QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
875             if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
876                 QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
877                 addDependency(data);
878                 m_qmldirs << data;
879             }
880         } else if (import.type == QDeclarativeScriptParser::Import::Script) {
881             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
882             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
883             addDependency(blob);
884
885             ScriptReference ref;
886             ref.location = import.location.start;
887             ref.qualifier = import.qualifier;
888             ref.script = blob;
889             blob->addref();
890             m_scripts << ref;
891
892         }
893     }
894
895     if (!finalUrl().scheme().isEmpty()) {
896         QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
897         if (QDeclarativeEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
898             QDeclarativeQmldirData *data = typeLoader()->getQmldir(importUrl);
899             addDependency(data);
900             m_qmldirs << data;
901         }
902     }
903 }
904
905 void QDeclarativeTypeData::allDependenciesDone()
906 {
907     if (!m_typesResolved) {
908         resolveTypes();
909         m_typesResolved = true;
910     }
911 }
912
913 void QDeclarativeTypeData::downloadProgressChanged(qreal p)
914 {
915     for (int ii = 0; ii < m_callbacks.count(); ++ii) {
916         TypeDataCallback *callback = m_callbacks.at(ii);
917         callback->typeDataProgress(this, p);
918     }
919 }
920
921 void QDeclarativeTypeData::compile()
922 {
923     Q_ASSERT(m_compiledData == 0);
924     QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Compiling);
925
926     m_compiledData = new QDeclarativeCompiledData(typeLoader()->engine());
927     m_compiledData->url = m_imports.baseUrl();
928     m_compiledData->name = m_compiledData->url.toString();
929     QDeclarativeDebugTrace::rangeData(QDeclarativeDebugTrace::Compiling, m_compiledData->name);
930
931     QDeclarativeCompiler compiler;
932     if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) {
933         setError(compiler.errors());
934         m_compiledData->release();
935         m_compiledData = 0;
936     }
937     QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Compiling);
938 }
939
940 void QDeclarativeTypeData::resolveTypes()
941 {
942     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
943     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
944
945     // For local urls, add an implicit import "." as first (most overridden) lookup. 
946     // This will also trigger the loading of the qmldir and the import of any native 
947     // types from available plugins.
948     QList<QDeclarativeError> errors;
949     if (QDeclarativeQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
950         m_imports.addImport(importDatabase, QLatin1String("."),
951                             QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
952                             qmldir->dirComponents(), &errors);
953     } else {
954         m_imports.addImport(importDatabase, QLatin1String("."), 
955                             QString(), -1, -1, QDeclarativeScriptParser::Import::File, 
956                             QDeclarativeDirComponents(), &errors);
957     }
958
959     // remove any errors which are due to the implicit import which aren't real errors.
960     // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
961     QList<QDeclarativeError> realErrors;
962     for (int i = 0; i < errors.size(); ++i) {
963         if (errors.at(i).description() != QDeclarativeImportDatabase::tr("import \".\" has no qmldir and no namespace")
964                 && errors.at(i).description() != QDeclarativeImportDatabase::tr("\".\": no such directory")) {
965             realErrors.prepend(errors.at(i)); // this is a real error.
966         }
967     }
968
969     // report any real errors which occurred during plugin loading or qmldir parsing.
970     if (!realErrors.isEmpty()) {
971         setError(realErrors);
972         return;
973     }
974
975     foreach (const QDeclarativeScriptParser::Import &import, scriptParser.imports()) {
976         QDeclarativeDirComponents qmldircomponentsnetwork;
977         if (import.type == QDeclarativeScriptParser::Import::Script)
978             continue;
979
980         if (import.type == QDeclarativeScriptParser::Import::File && import.qualifier.isEmpty()) {
981             QUrl qmldirUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
982             if (QDeclarativeQmldirData *qmldir = qmldirForUrl(qmldirUrl))
983                 qmldircomponentsnetwork = qmldir->dirComponents();
984         }
985
986         int vmaj = -1;
987         int vmin = -1;
988         import.extractVersion(&vmaj, &vmin);
989
990         QList<QDeclarativeError> errors;
991         if (!m_imports.addImport(importDatabase, import.uri, import.qualifier,
992                                  vmaj, vmin, import.type, qmldircomponentsnetwork, &errors)) {
993             QDeclarativeError error;
994             if (errors.size()) {
995                 error = errors.takeFirst();
996             } else {
997                 // this should not be possible!
998                 // Description should come from error provided by addImport() function.
999                 error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1000             }
1001             error.setUrl(m_imports.baseUrl());
1002             error.setLine(import.location.start.line);
1003             error.setColumn(import.location.start.column);
1004             errors.prepend(error); // put it back on the list after filling out information.
1005
1006             setError(errors);
1007             return;
1008         }
1009     }
1010
1011     foreach (QDeclarativeScriptParser::TypeReference *parserRef, scriptParser.referencedTypes()) {
1012         QByteArray typeName = parserRef->name.toUtf8();
1013
1014         TypeReference ref;
1015
1016         QUrl url;
1017         int majorVersion;
1018         int minorVersion;
1019         QDeclarativeImportedNamespace *typeNamespace = 0;
1020         QList<QDeclarativeError> errors;
1021
1022         if (!m_imports.resolveType(typeName, &ref.type, &url, &majorVersion, &minorVersion,
1023                                    &typeNamespace, &errors) || typeNamespace) {
1024             // Known to not be a type:
1025             //  - known to be a namespace (Namespace {})
1026             //  - type with unknown namespace (UnknownNamespace.SomeType {})
1027             QDeclarativeError error;
1028             QString userTypeName = parserRef->name;
1029             userTypeName.replace(QLatin1Char('/'),QLatin1Char('.'));
1030             if (typeNamespace) {
1031                 error.setDescription(QDeclarativeTypeLoader::tr("Namespace %1 cannot be used as a type").arg(userTypeName));
1032             } else {
1033                 if (errors.size()) {
1034                     error = errors.takeFirst();
1035                 } else {
1036                     // this should not be possible!
1037                     // Description should come from error provided by addImport() function.
1038                     error.setDescription(QDeclarativeTypeLoader::tr("Unreported error adding script import to import database"));
1039                 }
1040                 error.setUrl(m_imports.baseUrl());
1041                 error.setDescription(QDeclarativeTypeLoader::tr("%1 %2").arg(userTypeName).arg(error.description()));
1042             }
1043
1044             if (!parserRef->refObjects.isEmpty()) {
1045                 QDeclarativeParser::Object *obj = parserRef->refObjects.first();
1046                 error.setLine(obj->location.start.line);
1047                 error.setColumn(obj->location.start.column);
1048             }
1049
1050             errors.prepend(error);
1051             setError(errors);
1052             return;
1053         }
1054
1055         if (ref.type) {
1056             ref.majorVersion = majorVersion;
1057             ref.minorVersion = minorVersion;
1058         } else {
1059             ref.typeData = typeLoader()->get(url);
1060             addDependency(ref.typeData);
1061         }
1062
1063         if (parserRef->refObjects.count())
1064             ref.location = parserRef->refObjects.first()->location.start;
1065
1066         m_types << ref;
1067     }
1068 }
1069
1070 QDeclarativeQmldirData *QDeclarativeTypeData::qmldirForUrl(const QUrl &url)
1071 {
1072     for (int ii = 0; ii < m_qmldirs.count(); ++ii) {
1073         if (m_qmldirs.at(ii)->url() == url)
1074             return m_qmldirs.at(ii);
1075     }
1076     return 0;
1077 }
1078
1079 QDeclarativeScriptData::QDeclarativeScriptData(QDeclarativeEngine *engine)
1080 : QDeclarativeCleanup(engine), importCache(0), pragmas(QDeclarativeParser::Object::ScriptBlock::None),
1081   m_loaded(false)
1082 {
1083 }
1084
1085 QDeclarativeScriptData::~QDeclarativeScriptData()
1086 {
1087     clear();
1088 }
1089
1090 void QDeclarativeScriptData::clear()
1091 {
1092     if (importCache) {
1093         importCache->release();
1094         importCache = 0;
1095     }
1096
1097     for (int ii = 0; ii < scripts.count(); ++ii)
1098         scripts.at(ii)->release();
1099     scripts.clear();
1100
1101     qPersistentDispose(m_program);
1102     qPersistentDispose(m_value);
1103 }
1104
1105 QDeclarativeScriptBlob::QDeclarativeScriptBlob(const QUrl &url, QDeclarativeTypeLoader *loader)
1106 : QDeclarativeDataBlob(url, JavaScriptFile), m_pragmas(QDeclarativeParser::Object::ScriptBlock::None),
1107   m_scriptData(0), m_typeLoader(loader)
1108 {
1109 }
1110
1111 QDeclarativeScriptBlob::~QDeclarativeScriptBlob()
1112 {
1113     if (m_scriptData) {
1114         m_scriptData->release();
1115         m_scriptData = 0;
1116     }
1117 }
1118
1119 QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptBlob::pragmas() const
1120 {
1121     return m_pragmas;
1122 }
1123
1124 QString QDeclarativeScriptBlob::scriptSource() const
1125 {
1126     return m_source;
1127 }
1128
1129 QDeclarativeTypeLoader *QDeclarativeScriptBlob::typeLoader() const
1130 {
1131     return m_typeLoader;
1132 }
1133
1134 const QDeclarativeImports &QDeclarativeScriptBlob::imports() const
1135 {
1136     return m_imports;
1137 }
1138
1139 QDeclarativeScriptData *QDeclarativeScriptBlob::scriptData() const
1140 {
1141     return m_scriptData;
1142 }
1143
1144 void QDeclarativeScriptBlob::dataReceived(const QByteArray &data)
1145 {
1146     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(m_typeLoader->engine());
1147     QDeclarativeImportDatabase *importDatabase = &ep->importDatabase;
1148
1149     m_source = QString::fromUtf8(data);
1150
1151     QDeclarativeScriptParser::JavaScriptMetaData metadata =
1152         QDeclarativeScriptParser::extractMetaData(m_source);
1153
1154     m_imports.setBaseUrl(finalUrl());
1155
1156     m_pragmas = metadata.pragmas;
1157
1158     foreach (const QDeclarativeScriptParser::Import &import, metadata.imports) {
1159         Q_ASSERT(import.type != QDeclarativeScriptParser::Import::File);
1160
1161         if (import.type == QDeclarativeScriptParser::Import::Script) {
1162             QUrl scriptUrl = finalUrl().resolved(QUrl(import.uri));
1163             QDeclarativeScriptBlob *blob = typeLoader()->getScript(scriptUrl);
1164             addDependency(blob);
1165
1166             ScriptReference ref;
1167             ref.location = import.location.start;
1168             ref.qualifier = import.qualifier;
1169             ref.script = blob;
1170             blob->addref();
1171             m_scripts << ref;
1172         } else {
1173             Q_ASSERT(import.type == QDeclarativeScriptParser::Import::Library);
1174             int vmaj = -1;
1175             int vmin = -1;
1176             import.extractVersion(&vmaj, &vmin);
1177
1178             QList<QDeclarativeError> errors;
1179             if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
1180                                      import.type, QDeclarativeDirComponents(), &errors)) {
1181                 QDeclarativeError error = errors.takeFirst();
1182                 // description should be set by addImport().
1183                 error.setUrl(m_imports.baseUrl());
1184                 error.setLine(import.location.start.line);
1185                 error.setColumn(import.location.start.column);
1186                 errors.prepend(error);
1187
1188                 setError(errors);
1189                 return;
1190             }
1191         }
1192     }
1193 }
1194
1195 void QDeclarativeScriptBlob::done()
1196 {
1197     // Check all script dependencies for errors
1198     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1199         const ScriptReference &script = m_scripts.at(ii);
1200         Q_ASSERT(script.script->isCompleteOrError());
1201         if (script.script->isError()) {
1202             QList<QDeclarativeError> errors = script.script->errors();
1203             QDeclarativeError error;
1204             error.setUrl(finalUrl());
1205             error.setLine(script.location.line);
1206             error.setColumn(script.location.column);
1207             error.setDescription(typeLoader()->tr("Script %1 unavailable").arg(script.script->url().toString()));
1208             errors.prepend(error);
1209             setError(errors);
1210         }
1211     }
1212
1213     if (isError())
1214         return;
1215
1216     QDeclarativeEngine *engine = typeLoader()->engine();
1217     m_scriptData = new QDeclarativeScriptData(engine);
1218     m_scriptData->url = finalUrl();
1219     m_scriptData->importCache = new QDeclarativeTypeNameCache(engine);
1220
1221     for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
1222         const ScriptReference &script = m_scripts.at(ii);
1223
1224         m_scriptData->scripts.append(script.script);
1225         m_scriptData->importCache->add(script.qualifier, ii);
1226     }
1227
1228     m_imports.populateCache(m_scriptData->importCache, engine);
1229
1230     m_scriptData->pragmas = m_pragmas;
1231
1232     // XXX TODO: Handle errors that occur duing the script compile
1233     QV8Engine *v8engine = QDeclarativeEnginePrivate::get(engine)->v8engine();
1234     v8::HandleScope handle_scope;
1235     v8::Context::Scope scope(v8engine->context());
1236     v8::Local<v8::Script> program = v8engine->qmlModeCompile(m_source, finalUrl().toString(), 1);
1237     m_scriptData->m_program = qPersistentNew<v8::Script>(program);
1238 }
1239
1240 QDeclarativeQmldirData::QDeclarativeQmldirData(const QUrl &url)
1241 : QDeclarativeDataBlob(url, QmldirFile)
1242 {
1243 }
1244
1245 const QDeclarativeDirComponents &QDeclarativeQmldirData::dirComponents() const
1246 {
1247     return m_components;
1248 }
1249
1250 void QDeclarativeQmldirData::dataReceived(const QByteArray &data)
1251 {
1252     QDeclarativeDirParser parser;
1253     parser.setSource(QString::fromUtf8(data));
1254     parser.parse();
1255     m_components = parser.components();
1256 }
1257
1258 QT_END_NAMESPACE
1259