Lazily create QMetaObjects
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmltypeloader_p.h
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #ifndef QQMLTYPELOADER_P_H
43 #define QQMLTYPELOADER_P_H
44
45 //
46 //  W A R N I N G
47 //  -------------
48 //
49 // This file is not part of the Qt API.  It exists purely as an
50 // implementation detail.  This header file may change from version to
51 // version without notice, or even be removed.
52 //
53 // We mean it.
54 //
55
56 #include <QtCore/qobject.h>
57 #include <QtCore/qatomic.h>
58 #include <QtNetwork/qnetworkreply.h>
59 #include <QtQml/qqmlerror.h>
60 #include <QtQml/qqmlengine.h>
61 #include <QtQml/qqmlfile.h>
62
63 #include <private/qv8_p.h>
64 #include <private/qhashedstring_p.h>
65 #include <private/qqmlscript_p.h>
66 #include <private/qqmlimport_p.h>
67 #include <private/qqmlcleanup_p.h>
68 #include <private/qqmldirparser_p.h>
69 #include <private/qqmlbundle_p.h>
70 #include <private/qflagpointer_p.h>
71
72 QT_BEGIN_NAMESPACE
73
74 class QQmlScriptData;
75 class QQmlScriptBlob;
76 class QQmlQmldirData;
77 class QQmlTypeLoader;
78 class QQmlCompiledData;
79 class QQmlComponentPrivate;
80 class QQmlTypeData;
81 class QQmlDataLoader;
82 class QQmlExtensionInterface;
83
84 class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount
85 {
86 public:
87     enum Status {
88         Null,                    // Prior to QQmlDataLoader::load()
89         Loading,                 // Prior to data being received and dataReceived() being called
90         WaitingForDependencies,  // While there are outstanding addDependency()s
91         Complete,                // Finished
92         Error                    // Error
93     };
94
95     enum Type {
96         QmlFile,
97         JavaScriptFile,
98         QmldirFile
99     };
100
101     QQmlDataBlob(const QUrl &, Type);
102     virtual ~QQmlDataBlob();
103
104     Type type() const;
105
106     Status status() const;
107     bool isNull() const;
108     bool isLoading() const;
109     bool isWaiting() const;
110     bool isComplete() const;
111     bool isError() const;
112     bool isCompleteOrError() const;
113
114     qreal progress() const;
115
116     QUrl url() const;
117     QUrl finalUrl() const;
118     QString finalUrlString() const;
119
120     QList<QQmlError> errors() const;
121
122     class Data {
123     public:
124         inline const char *data() const;
125         inline int size() const;
126
127         inline QByteArray asByteArray() const;
128
129         inline bool isFile() const;
130         inline QQmlFile *asFile() const;
131
132     private:
133         friend class QQmlDataBlob;
134         friend class QQmlDataLoader;
135         inline Data();
136         Data(const Data &);
137         Data &operator=(const Data &);
138         QBiPointer<const QByteArray, QQmlFile> d;
139     };
140
141 protected:
142     // Can be called from within callbacks
143     void setError(const QQmlError &);
144     void setError(const QList<QQmlError> &errors);
145     void addDependency(QQmlDataBlob *);
146
147     // Callbacks made in load thread
148     virtual void dataReceived(const Data &) = 0;
149     virtual void done();
150     virtual void networkError(QNetworkReply::NetworkError);
151     virtual void dependencyError(QQmlDataBlob *);
152     virtual void dependencyComplete(QQmlDataBlob *);
153     virtual void allDependenciesDone();
154
155     // Callbacks made in main thread
156     virtual void downloadProgressChanged(qreal);
157     virtual void completed();
158 private:
159     friend class QQmlDataLoader;
160     friend class QQmlDataLoaderThread;
161
162     void tryDone();
163     void cancelAllWaitingFor();
164     void notifyAllWaitingOnMe();
165     void notifyComplete(QQmlDataBlob *);
166
167     struct ThreadData {
168         inline ThreadData();
169         inline QQmlDataBlob::Status status() const;
170         inline void setStatus(QQmlDataBlob::Status);
171         inline bool isAsync() const;
172         inline void setIsAsync(bool);
173         inline quint8 progress() const;
174         inline void setProgress(quint8);
175
176     private:
177         QAtomicInt _p;
178     };
179     ThreadData m_data;
180
181     // m_errors should *always* be written before the status is set to Error.
182     // We use the status change as a memory fence around m_errors so that locking
183     // isn't required.  Once the status is set to Error (or Complete), m_errors 
184     // cannot be changed.
185     QList<QQmlError> m_errors;
186
187     Type m_type;
188
189     QUrl m_url;
190     QUrl m_finalUrl;
191     mutable QString m_finalUrlString;
192
193     // List of QQmlDataBlob's that are waiting for me to complete.
194     QList<QQmlDataBlob *> m_waitingOnMe;
195
196     // List of QQmlDataBlob's that I am waiting for to complete.
197     QList<QQmlDataBlob *> m_waitingFor;
198
199     // Manager that is currently fetching data for me
200     QQmlDataLoader *m_manager;
201     int m_redirectCount:30;
202     bool m_inCallback:1;
203     bool m_isDone:1;
204 };
205
206 class QQmlDataLoaderThread;
207 class QQmlDataLoader
208 {
209 public:
210     QQmlDataLoader(QQmlEngine *);
211     ~QQmlDataLoader();
212
213     void lock();
214     void unlock();
215
216     bool isConcurrent() const { return true; }
217
218     enum Mode { PreferSynchronous, Asynchronous };
219
220     void load(QQmlDataBlob *, Mode = PreferSynchronous);
221     void loadWithStaticData(QQmlDataBlob *, const QByteArray &, Mode = PreferSynchronous);
222
223     QQmlEngine *engine() const;
224     void initializeEngine(QQmlExtensionInterface *, const char *);
225
226 private:
227     friend class QQmlDataBlob;
228     friend class QQmlDataLoaderThread;
229     friend class QQmlDataLoaderNetworkReplyProxy;
230
231     void loadThread(QQmlDataBlob *);
232     void loadWithStaticDataThread(QQmlDataBlob *, const QByteArray &);
233     void networkReplyFinished(QNetworkReply *);
234     void networkReplyProgress(QNetworkReply *, qint64, qint64);
235     
236     typedef QHash<QNetworkReply *, QQmlDataBlob *> NetworkReplies;
237
238     void setData(QQmlDataBlob *, const QByteArray &);
239     void setData(QQmlDataBlob *, QQmlFile *);
240     void setData(QQmlDataBlob *, const QQmlDataBlob::Data &);
241
242     QQmlEngine *m_engine;
243     QQmlDataLoaderThread *m_thread;
244     NetworkReplies m_networkReplies;
245 };
246
247 class QQmlBundleData : public QQmlBundle,
248                        public QQmlRefCount
249 {
250 public:
251     QQmlBundleData(const QString &);
252     QString fileName;
253 };
254
255 class QQmlTypeLoader : public QQmlDataLoader
256 {
257     Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader)
258 public:
259     QQmlTypeLoader(QQmlEngine *);
260     ~QQmlTypeLoader();
261
262     enum Option {
263         None,
264         PreserveParser
265     };
266     Q_DECLARE_FLAGS(Options, Option)
267
268     QQmlTypeData *getType(const QUrl &url, Mode mode = PreferSynchronous);
269     QQmlTypeData *getType(const QByteArray &, const QUrl &url, Options = None);
270
271     QQmlScriptBlob *getScript(const QUrl &);
272     QQmlQmldirData *getQmldir(const QUrl &);
273
274     QQmlBundleData *getBundle(const QString &);
275     QQmlBundleData *getBundle(const QHashedStringRef &);
276     void addBundle(const QString &, const QString &);
277
278     QString absoluteFilePath(const QString &path);
279     bool directoryExists(const QString &path);
280     const QQmlDirParser *qmlDirParser(const QString &filePath, const QString &uriHint, QString *outUrl);
281
282     void clearCache();
283     void trimCache();
284
285     bool isTypeLoaded(const QUrl &url) const;
286     bool isScriptLoaded(const QUrl &url) const;
287
288 private:
289     void addBundleNoLock(const QString &, const QString &);
290     QString bundleIdForQmldir(const QString &qmldir, const QString &uriHint);
291
292     template<typename T>
293     struct TypedCallback
294     {
295         TypedCallback(T *object, void (T::*func)(QQmlTypeData *)) : o(object), mf(func) {}
296
297         static void redirect(void *arg, QQmlTypeData *type)
298         {
299             TypedCallback<T> *self = reinterpret_cast<TypedCallback<T> *>(arg);
300             ((self->o)->*(self->mf))(type);
301         }
302
303     private:
304         T *o;
305         void (T::*mf)(QQmlTypeData *);
306     };
307
308     struct DirParser : public QQmlDirParser { QString adjustedUrl; };
309
310     typedef QHash<QUrl, QQmlTypeData *> TypeCache;
311     typedef QHash<QUrl, QQmlScriptBlob *> ScriptCache;
312     typedef QHash<QUrl, QQmlQmldirData *> QmldirCache;
313     typedef QStringHash<bool> StringSet;
314     typedef QStringHash<StringSet*> ImportDirCache;
315     typedef QStringHash<DirParser*> ImportQmlDirCache;
316     typedef QStringHash<QQmlBundleData *> BundleCache;
317     typedef QStringHash<QString> QmldirBundleIdCache;
318
319     TypeCache m_typeCache;
320     ScriptCache m_scriptCache;
321     QmldirCache m_qmldirCache;
322     ImportDirCache m_importDirCache;
323     ImportQmlDirCache m_importQmlDirCache;
324     BundleCache m_bundleCache;
325     QmldirBundleIdCache m_qmldirBundleIdCache;
326 };
327
328 Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlTypeLoader::Options)
329
330 class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlDataBlob
331 {
332 public:
333     struct TypeReference
334     {
335         TypeReference() : type(0), majorVersion(0), minorVersion(0), typeData(0) {}
336
337         QQmlScript::Location location;
338         QQmlType *type;
339         int majorVersion;
340         int minorVersion;
341         QQmlTypeData *typeData;
342     };
343
344     struct ScriptReference
345     {
346         ScriptReference() : script(0) {}
347
348         QQmlScript::Location location;
349         QString qualifier;
350         QQmlScriptBlob *script;
351     };
352
353 private:
354     friend class QQmlTypeLoader;
355
356     QQmlTypeData(const QUrl &, QQmlTypeLoader::Options, QQmlTypeLoader *);
357
358 public:
359     ~QQmlTypeData();
360
361     QQmlTypeLoader *typeLoader() const;
362
363     const QQmlImports &imports() const;
364     const QQmlScript::Parser &parser() const;
365
366     const QList<TypeReference> &resolvedTypes() const;
367     const QList<ScriptReference> &resolvedScripts() const;
368     const QSet<QString> &namespaces() const;
369
370     QQmlCompiledData *compiledData() const;
371
372     // Used by QQmlComponent to get notifications
373     struct TypeDataCallback {
374         ~TypeDataCallback() {}
375         virtual void typeDataProgress(QQmlTypeData *, qreal) {}
376         virtual void typeDataReady(QQmlTypeData *) {}
377     };
378     void registerCallback(TypeDataCallback *);
379     void unregisterCallback(TypeDataCallback *);
380
381 protected:
382     virtual void done();
383     virtual void completed();
384     virtual void dataReceived(const Data &);
385     virtual void allDependenciesDone();
386     virtual void downloadProgressChanged(qreal);
387
388 private:
389     void resolveTypes();
390     void compile();
391
392     QQmlTypeLoader::Options m_options;
393
394     QQmlQmldirData *qmldirForUrl(const QUrl &);
395
396     QQmlScript::Parser scriptParser;
397     QQmlImports m_imports;
398
399     QList<ScriptReference> m_scripts;
400     QList<QQmlQmldirData *> m_qmldirs;
401
402     QSet<QString> m_namespaces;
403
404     QList<TypeReference> m_types;
405     bool m_typesResolved:1;
406
407     QQmlCompiledData *m_compiledData;
408
409     QList<TypeDataCallback *> m_callbacks;
410    
411     QQmlTypeLoader *m_typeLoader;
412 };
413
414 // QQmlScriptData instances are created, uninitialized, by the loader in the 
415 // load thread.  The first time they are used by the VME, they are initialized which
416 // creates their v8 objects and they are referenced and added to the  engine's cleanup
417 // list.  During QQmlCleanup::clear() all v8 resources are destroyed, and the 
418 // reference that was created is released but final deletion only occurs once all the
419 // references as released.  This is all intended to ensure that the v8 resources are
420 // only created and destroyed in the main thread :)
421 class Q_AUTOTEST_EXPORT QQmlScriptData : public QQmlCleanup, public QQmlRefCount
422 {
423 private:
424     friend class QQmlTypeLoader;
425
426     QQmlScriptData();
427
428 public:
429     ~QQmlScriptData();
430
431     QUrl url;
432     QString urlString;
433     QQmlTypeNameCache *importCache;
434     QList<QQmlScriptBlob *> scripts;
435     QQmlScript::Object::ScriptBlock::Pragmas pragmas;
436
437     bool isInitialized() const { return hasEngine(); }
438     void initialize(QQmlEngine *);
439
440 protected:
441     virtual void clear(); // From QQmlCleanup
442
443 private:
444     friend class QQmlVME;
445     friend class QQmlScriptBlob;
446
447     bool m_loaded;
448     QByteArray m_programSource;
449     v8::Persistent<v8::Script> m_program;
450     v8::Persistent<v8::Object> m_value;
451 };
452
453 class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlDataBlob
454 {
455 private:
456     friend class QQmlTypeLoader;
457
458     QQmlScriptBlob(const QUrl &, QQmlTypeLoader *);
459
460 public:
461     ~QQmlScriptBlob();
462
463     struct ScriptReference
464     {
465         ScriptReference() : script(0) {}
466
467         QQmlScript::Location location;
468         QString qualifier;
469         QString nameSpace;
470         QQmlScriptBlob *script;
471     };
472
473     QQmlScript::Object::ScriptBlock::Pragmas pragmas() const;
474
475     QQmlTypeLoader *typeLoader() const;
476     const QQmlImports &imports() const;
477
478     QQmlScriptData *scriptData() const;
479
480 protected:
481     virtual void dataReceived(const Data &);
482     virtual void done();
483
484 private:
485     QQmlScript::Object::ScriptBlock::Pragmas m_pragmas;
486     QString m_source;
487
488     QQmlImports m_imports;
489     QList<ScriptReference> m_scripts;
490     QQmlScriptData *m_scriptData;
491
492     QQmlTypeLoader *m_typeLoader;
493 };
494
495 class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlDataBlob
496 {
497 private:
498     friend class QQmlTypeLoader;
499
500     QQmlQmldirData(const QUrl &);
501
502 public:
503     const QQmlDirComponents &dirComponents() const;
504
505 protected:
506     virtual void dataReceived(const Data &);
507
508 private:
509     QQmlDirComponents m_components;
510 };
511
512 QQmlDataBlob::Data::Data()
513 {
514 }
515
516 const char *QQmlDataBlob::Data::data() const
517 {
518     Q_ASSERT(!d.isNull());
519
520     if (d.isT1()) return d.asT1()->constData();
521     else return d.asT2()->data();
522 }
523
524 int QQmlDataBlob::Data::size() const
525 {
526     Q_ASSERT(!d.isNull());
527
528     if (d.isT1()) return d.asT1()->size();
529     else return d.asT2()->size();
530 }
531
532 bool QQmlDataBlob::Data::isFile() const
533 {
534     return d.isT2();
535 }
536
537 QByteArray QQmlDataBlob::Data::asByteArray() const
538 {
539     Q_ASSERT(!d.isNull());
540
541     if (d.isT1()) return *d.asT1();
542     else return d.asT2()->dataByteArray();
543 }
544
545 QQmlFile *QQmlDataBlob::Data::asFile() const
546 {
547     if (d.isT2()) return d.asT2();
548     else return 0;
549 }
550
551 QT_END_NAMESPACE
552
553 #endif // QQMLTYPELOADER_P_H