Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5#include "qqmlengine_p.h"
6#include "qqmlengine.h"
7
8#include <private/qqmlcontext_p.h>
9#include <private/qqmlpluginimporter_p.h>
10#include <private/qqmlprofiler_p.h>
11#include <private/qqmlscriptdata_p.h>
12#include <private/qqmlsourcecoordinate_p.h>
13#include <private/qqmltype_p.h>
14#include <private/qqmltypedata_p.h>
15#include <private/qqmlvmemetaobject_p.h>
16#include <private/qqmlcomponent_p.h>
17
18#include <QtQml/qqml.h>
19#include <QtQml/qqmlcomponent.h>
20#include <QtQml/qqmlcontext.h>
21#include <QtQml/qqmlincubator.h>
22#include <QtQml/qqmlscriptstring.h>
23
24#include <QtCore/qcoreapplication.h>
25#include <QtCore/qcryptographichash.h>
26#include <QtCore/qdir.h>
27#include <QtCore/qdiriterator.h>
28#include <QtCore/qmetaobject.h>
29#include <QtCore/qmutex.h>
30#include <QtCore/qstandardpaths.h>
31#include <QtCore/qstorageinfo.h>
32#include <QtCore/qthread.h>
33
34#if QT_CONFIG(qml_network)
35#include <QtQml/qqmlnetworkaccessmanagerfactory.h>
36#include <QtNetwork/qnetworkaccessmanager.h>
37#endif
38
39#ifdef Q_OS_WIN // for %APPDATA%
40# include <qt_windows.h>
41# include <shlobj.h>
42# include <QtCore/qlibrary.h>
43# ifndef CSIDL_APPDATA
44# define CSIDL_APPDATA 0x001a // <username>\Application Data
45# endif
46#endif // Q_OS_WIN
47
48#ifdef Q_OS_DARWIN
49# include <unistd.h>
50#endif
51
53
54using namespace Qt::StringLiterals;
55
57
58/*!
59 \qmltype QtObject
60 \nativetype QObject
61 \inqmlmodule QtQml
62 \ingroup qml-utility-elements
63 \brief A basic QML type.
64
65 The QtObject type is a non-visual element which contains only the
66 objectName property.
67
68 It can be useful to create a QtObject if you need an extremely
69 lightweight type to enclose a set of custom properties:
70
71 \snippet qml/qtobject.qml 0
72
73 It can also be useful for C++ integration, as it is just a plain
74 QObject. See the QObject documentation for further details.
75*/
76/*!
77 \qmlproperty string QtObject::objectName
78 This property holds the QObject::objectName for this specific object instance.
79
80 This allows a C++ application to locate an item within a QML component
81 using the QObject::findChild() method. For example, the following C++
82 application locates the child \l Rectangle item and dynamically changes its
83 \c color value:
84
85 \qml
86 // MyRect.qml
87
88 import QtQuick 2.0
89
90 Item {
91 width: 200; height: 200
92
93 Rectangle {
94 anchors.fill: parent
95 color: "red"
96 objectName: "myRect"
97 }
98 }
99 \endqml
100
101 \code
102 // main.cpp
103
104 QQuickView view;
105 view.setSource(QUrl::fromLocalFile("MyRect.qml"));
106 view.show();
107
108 QQuickItem *item = view.rootObject()->findChild<QQuickItem*>("myRect");
109 if (item)
110 item->setProperty("color", QColor(Qt::yellow));
111 \endcode
112*/
113
114Q_CONSTINIT std::atomic<bool> QQmlEnginePrivate::qml_debugging_enabled{false};
115bool QQmlEnginePrivate::s_designerMode = false;
116
117bool QQmlEnginePrivate::designerMode()
118{
119 return s_designerMode;
120}
121
122void QQmlEnginePrivate::activateDesignerMode()
123{
124 s_designerMode = true;
125}
126
127
128/*!
129 \class QQmlImageProviderBase
130 \brief The QQmlImageProviderBase class is used to register image providers in the QML engine.
131 \inmodule QtQml
132
133 Image providers must be registered with the QML engine. The only information the QML
134 engine knows about image providers is the type of image data they provide. To use an
135 image provider to acquire image data, you must cast the QQmlImageProviderBase pointer
136 to a QQuickImageProvider pointer.
137
138 \sa QQuickImageProvider, QQuickTextureFactory
139*/
140
141/*!
142 \enum QQmlImageProviderBase::ImageType
143
144 Defines the type of image supported by this image provider.
145
146 \value Image The Image Provider provides QImage images.
147 The QQuickImageProvider::requestImage() method will be called for all image requests.
148 \value Pixmap The Image Provider provides QPixmap images.
149 The QQuickImageProvider::requestPixmap() method will be called for all image requests.
150 \value Texture The Image Provider provides QSGTextureProvider based images.
151 The QQuickImageProvider::requestTexture() method will be called for all image requests.
152 \value ImageResponse The Image provider provides QQuickTextureFactory based images.
153 Should only be used in QQuickAsyncImageProvider or its subclasses.
154 The QQuickAsyncImageProvider::requestImageResponse() method will be called for all image requests.
155 Since Qt 5.6
156 \omitvalue Invalid
157*/
158
159/*!
160 \enum QQmlImageProviderBase::Flag
161
162 Defines specific requirements or features of this image provider.
163
164 \value ForceAsynchronousImageLoading Ensures that image requests to the provider are
165 run in a separate thread, which allows the provider to spend as much time as needed
166 on producing the image without blocking the main thread.
167*/
168
169/*!
170 \fn QQmlImageProviderBase::imageType() const
171
172 Implement this method to return the image type supported by this image provider.
173*/
174
175/*!
176 \fn QQmlImageProviderBase::flags() const
177
178 Implement this to return the properties of this image provider.
179*/
180
181/*! \internal */
182QQmlImageProviderBase::QQmlImageProviderBase()
183{
184}
185
186/*! \internal */
187QQmlImageProviderBase::~QQmlImageProviderBase()
188{
189}
190
191QQmlEnginePrivate::~QQmlEnginePrivate()
192{
193 if (inProgressCreations)
194 qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations);
195
196 if (incubationController) incubationController->d = nullptr;
197 incubationController = nullptr;
198
199#if QT_CONFIG(qml_debug)
200 delete profiler;
201#endif
202}
203
204void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
205{
206 QObjectPrivate *p = QObjectPrivate::get(o);
207 if (QQmlData *d = QQmlData::get(p)) {
208 const auto invalidate = [](QQmlContextData *c) {c->invalidate();};
209 if (d->ownContext) {
210 d->ownContext->deepClearContextObject(o, invalidate, invalidate);
211 d->ownContext.reset();
212 d->context = nullptr;
213 Q_ASSERT(!d->outerContext || d->outerContext->contextObject() != o);
214 } else if (d->outerContext && d->outerContext->contextObject() == o) {
215 d->outerContext->deepClearContextObject(o, invalidate, invalidate);
216 }
217
218 if (d->hasVMEMetaObject || d->hasInterceptorMetaObject) {
219 // This is somewhat dangerous because another thread might concurrently
220 // try to resolve the dynamic metaobject. In practice this will then
221 // lead to either the code path that still returns the interceptor
222 // metaobject or the code path that returns the string casted one. Both
223 // is fine if you cannot actually touch the object itself. Since the
224 // other thread is obviously not synchronized to this one, it can't.
225 //
226 // In particular we do this when delivering the frameSwapped() signal
227 // in QQuickWindow. The handler for frameSwapped() is written in a way
228 // that is thread safe as long as QQuickWindow's dtor hasn't finished.
229 // QQuickWindow's dtor does synchronize with the render thread, but it
230 // runs _after_ qdeclarativeelement_destructor.
231 static_cast<QQmlInterceptorMetaObject *>(p->metaObject)->invalidate();
232 d->hasVMEMetaObject = d->hasInterceptorMetaObject = false;
233 }
234
235 // Mark this object as in the process of deletion to
236 // prevent it resolving in bindings
237 QQmlData::markAsDeleted(o);
238 }
239}
240
241template<>
242int qmlRegisterType<void>(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
243{
244 QQmlPrivate::RegisterType type = {
245 QQmlPrivate::RegisterType::CurrentVersion,
246 QMetaType(),
247 QMetaType(),
248 0, nullptr, nullptr,
249 QString(),
250 nullptr,
251 uri,
252 QTypeRevision::fromVersion(versionMajor, versionMinor),
253 qmlName,
254 nullptr,
255 nullptr,
256 nullptr,
257 -1,
258 -1,
259 -1,
260 nullptr,
261 nullptr,
262 nullptr,
263 QTypeRevision::zero(),
264 -1,
265 QQmlPrivate::ValueTypeCreationMethod::None,
266 };
267
268 return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
269}
270
271bool QQmlEnginePrivate::baseModulesUninitialized = true;
272void QQmlEnginePrivate::init()
273{
274 Q_Q(QQmlEngine);
275
276 if (baseModulesUninitialized) {
277 // Register builtins
278 qml_register_types_QML();
279
280 // No need to specifically register those.
281 static_assert(std::is_same_v<QStringList, QList<QString>>);
282 static_assert(std::is_same_v<QVariantList, QList<QVariant>>);
283
284 qRegisterMetaType<QQmlScriptString>();
285 qRegisterMetaType<QQmlComponent::Status>();
286 qRegisterMetaType<QList<QObject*> >();
287 qRegisterMetaType<QQmlBinding*>();
288
289 // Protect the module: We don't want any URL interceptor to mess with the builtins.
290 qmlProtectModule("QML", 1);
291
292 QQmlData::init();
293 baseModulesUninitialized = false;
294 }
295
296 q->handle()->setQmlEngine(q);
297
298 rootContext = new QQmlContext(q,true);
299}
300
301/*!
302 \class QQmlEngine
303 \since 5.0
304 \inmodule QtQml
305 \brief The QQmlEngine class provides an environment for instantiating QML components.
306
307 A QQmlEngine is used to manage \l{QQmlComponent}{components} and objects created from
308 them and execute their bindings and functions. QQmlEngine also inherits from
309 \l{QJSEngine} which allows seamless integration between your QML components and
310 JavaScript code.
311
312 Each QML component is instantiated in a QQmlContext. In QML, contexts are arranged
313 hierarchically and this hierarchy is managed by the QQmlEngine. By default,
314 components are instantiated in the \l {QQmlEngine::rootContext()}{root context}.
315
316 \sa QQmlComponent, QQmlContext, {QML Global Object}, QQmlApplicationEngine
317*/
318
319/*!
320 Create a new QQmlEngine with the given \a parent.
321*/
322QQmlEngine::QQmlEngine(QObject *parent)
323: QJSEngine(*new QQmlEnginePrivate, parent)
324{
325 Q_D(QQmlEngine);
326 d->init();
327 QJSEnginePrivate::addToDebugServer(this);
328}
329
330/*!
331* \internal
332*/
333QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent)
334: QJSEngine(dd, parent)
335{
336 Q_D(QQmlEngine);
337 d->init();
338}
339
340/*!
341 Destroys the QQmlEngine.
342
343 Any QQmlContext's created on this engine will be
344 invalidated, but not destroyed (unless they are parented to the
345 QQmlEngine object).
346
347 See ~QJSEngine() for details on cleaning up the JS engine.
348*/
349QQmlEngine::~QQmlEngine()
350{
351 Q_D(QQmlEngine);
352
353#if QT_CONFIG(qml_worker_script)
354 // Delete the workerscript engine early
355 // so that it won't be able to use the type loader anymore.
356 delete std::exchange(d->workerScriptEngine, nullptr);
357#endif
358
359 QV4::ExecutionEngine *v4 = handle();
360 v4->inShutdown = true;
361 QJSEnginePrivate::removeFromDebugServer(this);
362
363 // Emit onDestruction signals for the root context before
364 // we destroy the contexts, engine, Singleton Types etc. that
365 // may be required to handle the destruction signal.
366 QQmlContextPrivate::get(rootContext())->emitDestruction();
367
368 // clean up all singleton type instances which we own.
369 // we do this here and not in the private dtor since otherwise a crash can
370 // occur (if we are the QObject parent of the QObject singleton instance)
371 // XXX TODO: performance -- store list of singleton types separately?
372 d->singletonInstances.clear();
373
374 delete d->rootContext;
375 d->rootContext = nullptr;
376
377 v4->typeLoader()->invalidate();
378
379 // QQmlGadgetPtrWrapper can have QQmlData with various references.
380 qDeleteAll(d->cachedValueTypeInstances);
381 d->cachedValueTypeInstances.clear();
382
383 v4->resetQmlEngine();
384}
385
386/*! \fn void QQmlEngine::quit()
387 This signal is emitted when the QML loaded by the engine would like to quit.
388
389 \sa exit()
390 */
391
392/*! \fn void QQmlEngine::exit(int retCode)
393 This signal is emitted when the QML loaded by the engine would like to exit
394 from the event loop with the specified return code \a retCode.
395
396 \since 5.8
397 \sa quit()
398 */
399
400
401/*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings)
402 This signal is emitted when \a warnings messages are generated by QML.
403 */
404
405/*!
406 Clears the engine's internal component cache.
407
408 This function causes the property metadata of most components previously
409 loaded by the engine to be destroyed. It does so by dropping unreferenced
410 components from the engine's component cache. It does not drop components that
411 are still referenced since that would almost certainly lead to crashes further
412 down the line.
413
414 If no components are referenced, this function returns the engine to a state
415 where it does not contain any loaded component data. This may be useful in
416 order to reload a smaller subset of the previous component set, or to load a
417 new version of a previously loaded component.
418
419 Once the component cache has been cleared, components must be loaded before
420 any new objects can be created.
421
422 \note Any existing objects created from QML components retain their types,
423 even if you clear the component cache. This includes singleton objects. If you
424 create more objects from the same QML code after clearing the cache, the new
425 objects will be of different types than the old ones. Assigning such a new
426 object to a property of its declared type belonging to an object created
427 before clearing the cache won't work.
428
429 As a general rule of thumb, make sure that no objects created from QML
430 components are alive when you clear the component cache.
431
432 \sa trimComponentCache(), clearSingletons()
433 */
434void QQmlEngine::clearComponentCache()
435{
436 Q_D(QQmlEngine);
437
438 // QQmlGadgetPtrWrapper can have QQmlData with various references.
439 qDeleteAll(std::exchange(d->cachedValueTypeInstances, {}));
440
441 QV4::ExecutionEngine *v4 = handle();
442
443 // Reset the values of JavaScript libraries and ECMAScript modules
444 // So that they get re-evaluated on next usage.
445 {
446 const auto cus = v4->compilationUnits();
447 for (const auto &cu : cus) {
448 cu->setValue(QV4::Value::emptyValue());
449 delete[] std::exchange(cu->imports, nullptr);
450 }
451 }
452
453 // Contexts can hold on to CUs but live on the JS heap.
454 // Use a non-incremental GC run to get rid of those.
455 QV4::MemoryManager *mm = v4->memoryManager;
456 auto oldLimit = mm->gcStateMachine->timeLimit;
457 mm->setGCTimeLimit(-1);
458 mm->runGC();
459 mm->gcStateMachine->timeLimit = std::move(oldLimit);
460
461 v4->trimCompilationUnits();
462 v4->typeLoader()->clearCache();
463 QQmlMetaType::freeUnusedTypesAndCaches();
464}
465
466/*!
467 Trims the engine's internal component cache.
468
469 This function causes the property metadata of any loaded components which are
470 not currently in use to be destroyed.
471
472 A component is considered to be in use if there are any extant instances of
473 the component itself, any instances of other components that use the component,
474 or any objects instantiated by any of those components.
475
476 \sa clearComponentCache()
477 */
478void QQmlEngine::trimComponentCache()
479{
480 QV4::ExecutionEngine *v4 = handle();
481 v4->trimCompilationUnits();
482 v4->typeLoader()->trimCache();
483}
484
485/*!
486 Clears all singletons the engine owns.
487
488 This function drops all singleton instances, deleting any QObjects owned by
489 the engine among them. This is useful to make sure that no QML-created objects
490 are left before calling clearComponentCache().
491
492 QML properties holding QObject-based singleton instances become null if the
493 engine owns the singleton or retain their value if the engine doesn't own it.
494 The singletons are not automatically re-created by accessing existing
495 QML-created objects. Only when new components are instantiated, the singletons
496 are re-created.
497
498 \sa clearComponentCache()
499 */
500void QQmlEngine::clearSingletons()
501{
502 Q_D(QQmlEngine);
503 d->singletonInstances.clear();
504}
505
506/*!
507 Returns the engine's root context.
508
509 The root context is automatically created by the QQmlEngine.
510 Data that should be available to all QML component instances
511 instantiated by the engine should be put in the root context.
512
513 Additional data that should only be available to a subset of
514 component instances should be added to sub-contexts parented to the
515 root context.
516*/
517QQmlContext *QQmlEngine::rootContext() const
518{
519 Q_D(const QQmlEngine);
520 return d->rootContext;
521}
522
523#if QT_DEPRECATED_SINCE(6, 0)
524/*!
525 \internal
526 \deprecated
527 This API is private for 5.1
528
529 Returns the last QQmlAbstractUrlInterceptor. It must not be modified outside
530 the GUI thread.
531*/
532QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const
533{
534 return QQmlTypeLoader::get(this)->urlInterceptors().last();
535}
536#endif
537
538/*!
539 Adds a \a urlInterceptor to be used when resolving URLs in QML.
540 This also applies to URLs used for loading script files and QML types.
541 The URL interceptors should not be modifed while the engine is loading files,
542 or URL selection may be inconsistent. Multiple URL interceptors, when given,
543 will be called in the order they were added for each URL.
544
545 QQmlEngine does not take ownership of the interceptor and won't delete it.
546*/
547void QQmlEngine::addUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
548{
549 QQmlTypeLoader::get(this)->addUrlInterceptor(urlInterceptor);
550}
551
552/*!
553 Remove a \a urlInterceptor that was previously added using
554 \l addUrlInterceptor. The URL interceptors should not be modifed while the
555 engine is loading files, or URL selection may be inconsistent.
556
557 This does not delete the interceptor, but merely removes it from the engine.
558 You can re-use it on the same or a different engine afterwards.
559*/
560void QQmlEngine::removeUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor)
561{
562 QQmlTypeLoader::get(this)->removeUrlInterceptor(urlInterceptor);
563}
564
565/*!
566 Run the current URL interceptors on the given \a url of the given \a type and
567 return the result.
568 */
569QUrl QQmlEngine::interceptUrl(const QUrl &url, QQmlAbstractUrlInterceptor::DataType type) const
570{
571 return QQmlTypeLoader::get(this)->interceptUrl(url, type);
572}
573
574/*!
575 Returns the list of currently active URL interceptors.
576 */
577QList<QQmlAbstractUrlInterceptor *> QQmlEngine::urlInterceptors() const
578{
579 return QQmlTypeLoader::get(this)->urlInterceptors();
580}
581
582QSharedPointer<QQmlImageProviderBase> QQmlEnginePrivate::imageProvider(const QString &providerId) const
583{
584 const QString providerIdLower = providerId.toLower();
585 QMutexLocker locker(&imageProviderMutex);
586 return imageProviders.value(providerIdLower);
587}
588
589#if QT_CONFIG(qml_network)
590/*!
591 Sets the \a factory to use for creating QNetworkAccessManager(s).
592
593 QNetworkAccessManager is used for all network access by QML. By
594 implementing a factory it is possible to create custom
595 QNetworkAccessManager with specialized caching, proxy and cookie
596 support.
597
598 The factory must be set before executing the engine.
599
600 \note QQmlEngine does not take ownership of the factory.
601*/
602void QQmlEngine::setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *factory)
603{
604 QQmlTypeLoader::get(this)->setNetworkAccessManagerFactory(factory);
605}
606
607class QQmlEnginePublicAPIToken {};
608
609/*!
610 Returns the current QQmlNetworkAccessManagerFactory.
611
612 \sa setNetworkAccessManagerFactory()
613*/
614QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const
615{
616 return QQmlTypeLoader::get(this)->networkAccessManagerFactory().get(QQmlEnginePublicAPIToken());
617}
618
619/*!
620 Returns a common QNetworkAccessManager which can be used by any QML
621 type instantiated by this engine.
622
623 If a QQmlNetworkAccessManagerFactory has been set and a
624 QNetworkAccessManager has not yet been created, the
625 QQmlNetworkAccessManagerFactory will be used to create the
626 QNetworkAccessManager; otherwise the returned QNetworkAccessManager
627 will have no proxy or cache set.
628
629 \sa setNetworkAccessManagerFactory()
630*/
631QNetworkAccessManager *QQmlEngine::networkAccessManager() const
632{
633 return handle()->getNetworkAccessManager();
634}
635#endif // qml_network
636
637/*!
638
639 Sets the \a provider to use for images requested via the \e
640 image: url scheme, with host \a providerId. The QQmlEngine
641 takes ownership of \a provider.
642
643 Image providers enable support for pixmap and threaded image
644 requests. See the QQuickImageProvider documentation for details on
645 implementing and using image providers.
646
647 All required image providers should be added to the engine before any
648 QML sources files are loaded.
649
650 \sa removeImageProvider(), QQuickImageProvider, QQmlImageProviderBase
651*/
652void QQmlEngine::addImageProvider(const QString &providerId, QQmlImageProviderBase *provider)
653{
654 Q_D(QQmlEngine);
655 QString providerIdLower = providerId.toLower();
656 QSharedPointer<QQmlImageProviderBase> sp(provider);
657 QMutexLocker locker(&d->imageProviderMutex);
658 d->imageProviders.insert(std::move(providerIdLower), std::move(sp));
659}
660
661/*!
662 Returns the image provider set for \a providerId if found; otherwise returns \nullptr.
663
664 \sa QQuickImageProvider
665*/
666QQmlImageProviderBase *QQmlEngine::imageProvider(const QString &providerId) const
667{
668 Q_D(const QQmlEngine);
669 const QString providerIdLower = providerId.toLower();
670 QMutexLocker locker(&d->imageProviderMutex);
671 return d->imageProviders.value(providerIdLower).data();
672}
673
674/*!
675 Removes the image provider for \a providerId.
676
677 \sa addImageProvider(), QQuickImageProvider
678*/
679void QQmlEngine::removeImageProvider(const QString &providerId)
680{
681 Q_D(QQmlEngine);
682 const QString providerIdLower = providerId.toLower();
683 QMutexLocker locker(&d->imageProviderMutex);
684 d->imageProviders.take(providerIdLower);
685}
686
687/*!
688 Return the base URL for this engine. The base URL is only used to
689 resolve components when a relative URL is passed to the
690 QQmlComponent constructor.
691
692 If a base URL has not been explicitly set, this method returns the
693 application's current working directory.
694
695 \sa setBaseUrl()
696*/
697QUrl QQmlEngine::baseUrl() const
698{
699 Q_D(const QQmlEngine);
700 if (d->baseUrl.isEmpty()) {
701 const QString currentPath = QDir::currentPath();
702 const QString rootPath = QDir::rootPath();
703 return QUrl::fromLocalFile((currentPath == rootPath) ? rootPath : (currentPath + QDir::separator()));
704 } else {
705 return d->baseUrl;
706 }
707}
708
709/*!
710 Set the base URL for this engine to \a url.
711
712 \sa baseUrl()
713*/
714void QQmlEngine::setBaseUrl(const QUrl &url)
715{
716 Q_D(QQmlEngine);
717 d->baseUrl = url;
718}
719
720/*!
721 Returns true if warning messages will be output to stderr in addition
722 to being emitted by the warnings() signal, otherwise false.
723
724 The default value is true.
725*/
726bool QQmlEngine::outputWarningsToStandardError() const
727{
728 Q_D(const QQmlEngine);
729 return d->outputWarningsToMsgLog;
730}
731
732/*!
733 Set whether warning messages will be output to stderr to \a enabled.
734
735 If \a enabled is true, any warning messages generated by QML will be
736 output to stderr and emitted by the warnings() signal. If \a enabled
737 is false, only the warnings() signal will be emitted. This allows
738 applications to handle warning output themselves.
739
740 The default value is true.
741*/
742void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
743{
744 Q_D(QQmlEngine);
745 d->outputWarningsToMsgLog = enabled;
746}
747
748
749/*!
750 \since 6.6
751 If this method is called inside of a function that is part of
752 a binding in QML, the binding will be treated as a translation binding.
753
754 \code
755 class I18nAwareClass : public QObject {
756
757 //...
758
759 QString text() const
760 {
761 if (auto engine = qmlEngine(this))
762 engine->markCurrentFunctionAsTranslationBinding();
763 return tr("Hello, world!");
764 }
765 };
766 \endcode
767
768 \note This function is mostly useful if you wish to provide your
769 own alternative to the qsTr function. To ensure that properties
770 exposed from C++ classes are updated on language changes, it is
771 instead recommended to react to \c LanguageChange events. That
772 is a more general mechanism which also works when the class is
773 used in a non-QML context, and has slightly less overhead. However,
774 using \c markCurrentFunctionAsTranslationBinding can be acceptable
775 when the class is already closely tied to the QML engine.
776 For more details, see \l {Prepare for Dynamic Language Changes}
777
778 \sa QQmlEngine::retranslate
779*/
780void QQmlEngine::markCurrentFunctionAsTranslationBinding()
781{
782 Q_D(QQmlEngine);
783 if (auto propertyCapture = d->propertyCapture)
784 propertyCapture->captureTranslation();
785}
786
787/*!
788 \since 6.12
789 Set the singleton instance to use for the given type on the QML engine.
790
791 This function allows you to manually set a QObject-derived instance to use as
792 the singleton in QML for this engine. This allows you to control the creation
793 of the instance. This can be useful in several scenarios, including for
794 cases where your singleton needs to communicate with backend components.
795
796 This function takes the \a moduleName and \a typeName to indicate the
797 singleton type you are trying to set, and the \a instance to set. The type
798 has to be already registered as a QML singleton type, ideally by using
799 \l QML_ELEMENT and \l QML_SINGLETON. If the module has not already been
800 loaded, it will be now.
801
802 The function returns true on success or false on failure. If a
803 failure occurs, a warning is emitted detailing the failure.
804
805 As an example, the singleton might need a backend service to work,
806 and could then be declared as follows:
807 \snippet code/src_qml_qqmlengine.cpp 6
808
809 Upon initialization of the application, you can then do:
810 \snippet code/src_qml_qqmlengine.cpp 7
811
812 Note instead of providing a default constructor or a static create
813 function, the \l QML_UNCREATABLE() macro was used to indicate this
814 item cannot be created by the qml engine.
815
816 Singleton instances can only be set once per type and engine, and must
817 be set before any use. Once a singleton instance is created or set,
818 it is no longer possible to set it using this function, so you should set
819 the instances before they are first used from QML.
820
821 The engine will \e{not} take ownership of the instance you pass, unless
822 you explicitly instruct the engine to do so by using
823 \l QJSEngine::setObjectOwnership().
824
825 \warning Make sure the \a instance outlives the lifetime of the engine.
826*/
827bool QQmlEngine::setExternalSingletonInstance(QAnyStringView moduleName, QAnyStringView typeName, QObject *instance)
828{
829 Q_D(QQmlEngine);
830
831 const auto loadHelper = QQml::makeRefPointer<LoadHelper>(
832 QQmlTypeLoader::get(this), moduleName, typeName, QQmlTypeLoader::Synchronous);
833 const QQmlType type = loadHelper->type();
834
835 if (!type.isValid()) {
836 qWarning().noquote() << "Error setting singleton instance: type" << typeName << "in module" << moduleName << "is not valid";
837 return false;
838 }
839
840 if (!instance) {
841 qWarning() << "Error setting singleton instance: the instance cannot be a nullptr";
842 return false;
843 }
844 if (!type.isSingleton()) {
845 qWarning() << "Error setting singleton instance: the type" << type.elementName() << "is not declared as a singleton type";
846 return false;
847 }
848 const QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
849 Q_ASSERT(siinfo != nullptr);
850 QJSValue value = d->singletonInstances.value(siinfo);
851 if (!value.isUndefined()) {
852 qWarning() << "Error setting singleton instance: there already is an instance for this singleton";
853 return false;
854 }
855
856 const auto baseMetaObject = type.baseMetaObject();
857 if (!(baseMetaObject && instance->metaObject()->inherits(baseMetaObject))) {
858 qWarning() << "Error setting singleton instance: the meta type of the instance" << instance->metaObject()->className()
859 << "does not match the type of the registered singleton"
860 << (baseMetaObject ? baseMetaObject->className() : "(unknown)"); //be careful to assume baseMetaObject is valid
861 return false;
862 }
863
864 QQmlData *data = QQmlData::get(instance, true);
865 if (!data->explicitIndestructibleSet) {
866 // Unless already explicitly set, set it up so that the engine won't delete
867 // the object.
868 data->explicitIndestructibleSet = true;
869 data->indestructible = true;
870 }
871 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
872 // should behave identically to QML singleton types. You can, however, manually
873 // assign a context; and clearSingletons() retains the contexts, in which case
874 // we don't want to see warnings about the object already having a context.
875 if (!data->context) {
876 auto contextData = QQmlContextData::get(new QQmlContext(rootContext(), this));
877 data->context = contextData.data();
878 contextData->addOwnedObject(data);
879 }
880
881 value = newQObject(instance);
882 d->singletonInstances.convertAndInsert(d->v4Engine.get(), siinfo, &value);
883
884 return true;
885}
886
887/*!
888 \internal
889
890 Capture the given property as part of a binding.
891 */
892void QQmlEngine::captureProperty(QObject *object, const QMetaProperty &property) const
893{
894 Q_D(const QQmlEngine);
895 if (d->propertyCapture && !property.isConstant()) {
896 d->propertyCapture->captureProperty(
897 object, property.propertyIndex(),
898 QMetaObjectPrivate::signalIndex(property.notifySignal()));
899 }
900}
901
902/*!
903 \qmlproperty string Qt::uiLanguage
904 \since 5.15
905
906 The uiLanguage holds the name of the language to be used for user interface
907 string translations. It is exposed in C++ as \l QJSEngine::uiLanguage property.
908
909 You can set the value freely and use it in bindings. It is recommended to set it
910 after installing translators in your application. By convention, an empty string
911 means no translation from the language used in the source code is intended to occur.
912
913 If you're using QQmlApplicationEngine and the value changes, QQmlEngine::retranslate()
914 will be called.
915*/
916
917/*!
918 \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId)
919
920 Returns the instance of a singleton type that was registered under \a qmlTypeId.
921
922 The template argument \e T may be either QJSValue or a pointer to a QObject-derived
923 type and depends on how the singleton was registered. If no instance of \e T has been
924 created yet, it is created now. If \a qmlTypeId does not represent a valid singleton
925 type, either a default constructed QJSValue or a \c nullptr is returned.
926
927 QObject* example:
928
929 \snippet code/src_qml_qqmlengine.cpp 0
930 \codeline
931 \snippet code/src_qml_qqmlengine.cpp 1
932 \codeline
933 \snippet code/src_qml_qqmlengine.cpp 2
934
935 QJSValue example:
936
937 \snippet code/src_qml_qqmlengine.cpp 3
938 \codeline
939 \snippet code/src_qml_qqmlengine.cpp 4
940
941 It is recommended to store the QML type id, e.g. as a static member in the
942 singleton class. The lookup via qmlTypeId() is costly.
943
944 \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
945 \since 5.12
946*/
947template<>
948QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
949{
950 Q_D(QQmlEngine);
951 QQmlType type = QQmlMetaType::qmlTypeById(qmlTypeId);
952
953 if (!type.isValid()) {
954 qWarning().noquote() << "Singleton instance: type with id" << qmlTypeId << "is not valid";
955 return {};
956 }
957
958 if (!type.isSingleton()) {
959 qWarning() << "Singleton instance: type" << type.elementName() << "with id" << qmlTypeId << "is not declared as a singleton type";
960 return {};
961 }
962
963 return d->singletonInstance<QJSValue>(type);
964}
965
966
967/*!
968 \fn template<typename T> T QQmlEngine::singletonInstance(QAnyStringView uri, QAnyStringView typeName)
969
970 \overload
971 Returns the instance of a singleton type named \a typeName from the module specified by \a uri.
972
973 This method can be used as an alternative to calling qmlTypeId followed by the id based overload of
974 singletonInstance. This is convenient when one only needs to do a one time setup of a
975 singleton; if repeated access to the singleton is required, caching its typeId will allow
976 faster subsequent access via the
977 \l {QQmlEngine::singletonInstance(int qmlTypeId)}{type-id based overload}.
978
979 The template argument \e T may be either QJSValue or a pointer to a QObject-derived
980 type and depends on how the singleton was registered. If no instance of \e T has been
981 created yet, it is created now. If \a typeName does not represent a valid singleton
982 type, either a default constructed QJSValue or a \c nullptr is returned.
983
984 \snippet code/src_qml_qqmlengine.cpp 5
985
986 \sa QML_SINGLETON, qmlRegisterSingletonType(), qmlTypeId()
987 \since 6.5
988*/
989template<>
990QJSValue QQmlEngine::singletonInstance<QJSValue>(QAnyStringView uri, QAnyStringView typeName)
991{
992 Q_D(QQmlEngine);
993
994 auto loadHelper = QQml::makeRefPointer<LoadHelper>(
995 QQmlTypeLoader::get(this), uri, typeName, QQmlTypeLoader::Synchronous);
996 const QQmlType type = loadHelper->type();
997
998 if (!type.isValid()) {
999 qWarning().noquote() << "Singleton instance: type" << typeName << "in module" << uri << "is not valid";
1000 return {};
1001 }
1002
1003 if (!type.isSingleton()) {
1004 qWarning() << "Singleton instance: type" << type.elementName() << "is not declared as a singleton type";
1005 return {};
1006 }
1007
1008 return d->singletonInstance<QJSValue>(type);
1009}
1010
1011/*!
1012 Refreshes all binding expressions that use strings marked for translation.
1013
1014 Call this function after you have installed a new translator with
1015 QCoreApplication::installTranslator, to ensure that your user-interface
1016 shows up-to-date translations.
1017
1018 \since 5.10
1019*/
1020void QQmlEngine::retranslate()
1021{
1022 Q_D(QQmlEngine);
1023 d->translationLanguage.notify();
1024}
1025
1026/*!
1027 Returns the QQmlContext for the \a object, or nullptr if no
1028 context has been set.
1029
1030 When the QQmlEngine instantiates a QObject, an internal context is assigned
1031 to it automatically. Such internal contexts are read-only. You cannot set
1032 context properties on them.
1033
1034 \sa qmlContext(), qmlEngine(), QQmlContext::setContextProperty()
1035 */
1036QQmlContext *QQmlEngine::contextForObject(const QObject *object)
1037{
1038 if(!object)
1039 return nullptr;
1040
1041 QQmlData *data = QQmlData::get(object);
1042 if (data && data->outerContext)
1043 return data->outerContext->asQQmlContext();
1044
1045 return nullptr;
1046}
1047
1048/*!
1049 Sets the QQmlContext for the \a object to \a context.
1050 If the \a object already has a context, a warning is
1051 output, but the context is not changed.
1052
1053 When the QQmlEngine instantiates a QObject, the context is
1054 set automatically.
1055 */
1056void QQmlEngine::setContextForObject(QObject *object, QQmlContext *context)
1057{
1058 if (!object || !context)
1059 return;
1060
1061 QQmlData *data = QQmlData::get(object, true);
1062 if (data->context) {
1063 qWarning("QQmlEngine::setContextForObject(): Object already has a QQmlContext");
1064 return;
1065 }
1066
1067 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
1068 Q_ASSERT(data->context == nullptr);
1069 data->context = contextData.data();
1070 contextData->addOwnedObject(data);
1071}
1072
1073/*!
1074 \reimp
1075*/
1076bool QQmlEngine::event(QEvent *e)
1077{
1078 if (e->type() == QEvent::LanguageChange) {
1079 retranslate();
1080 }
1081
1082 return QJSEngine::event(e);
1083}
1084
1085void QQmlEnginePrivate::sendQuit()
1086{
1087 Q_Q(QQmlEngine);
1088 emit q->quit();
1089 if (q->receivers(SIGNAL(quit())) == 0) {
1090 qWarning("Signal QQmlEngine::quit() emitted, but no receivers connected to handle it.");
1091 }
1092}
1093
1094void QQmlEnginePrivate::sendExit(int retCode)
1095{
1096 Q_Q(QQmlEngine);
1097 if (q->receivers(SIGNAL(exit(int))) == 0)
1098 qWarning("Signal QQmlEngine::exit() emitted, but no receivers connected to handle it.");
1099 emit q->exit(retCode);
1100}
1101
1102static void dumpwarning(const QQmlError &error)
1103{
1104 switch (error.messageType()) {
1105 case QtDebugMsg:
1106 QMessageLogger(error.url().toString().toLatin1().constData(),
1107 error.line(), nullptr).debug().noquote().nospace()
1108 << error.toString();
1109 break;
1110 case QtInfoMsg:
1111 QMessageLogger(error.url().toString().toLatin1().constData(),
1112 error.line(), nullptr).info().noquote().nospace()
1113 << error.toString();
1114 break;
1115 case QtWarningMsg:
1116 case QtFatalMsg: // fatal does not support streaming, and furthermore, is actually fatal. Probably not desirable for QML.
1117 QMessageLogger(error.url().toString().toLatin1().constData(),
1118 error.line(), nullptr).warning().noquote().nospace()
1119 << error.toString();
1120 break;
1121 case QtCriticalMsg:
1122 QMessageLogger(error.url().toString().toLatin1().constData(),
1123 error.line(), nullptr).critical().noquote().nospace()
1124 << error.toString();
1125 break;
1126 }
1127}
1128
1129static void dumpwarning(const QList<QQmlError> &errors)
1130{
1131 for (int ii = 0; ii < errors.size(); ++ii)
1132 dumpwarning(errors.at(ii));
1133}
1134
1135void QQmlEnginePrivate::warning(const QQmlError &error)
1136{
1137 Q_Q(QQmlEngine);
1138 emit q->warnings(QList<QQmlError>({error}));
1139 if (outputWarningsToMsgLog)
1140 dumpwarning(error);
1141}
1142
1143void QQmlEnginePrivate::warning(const QList<QQmlError> &errors)
1144{
1145 Q_Q(QQmlEngine);
1146 emit q->warnings(errors);
1147 if (outputWarningsToMsgLog)
1148 dumpwarning(errors);
1149}
1150
1151void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error)
1152{
1153 if (engine)
1154 QQmlEnginePrivate::get(engine)->warning(error);
1155 else
1156 dumpwarning(error);
1157}
1158
1159void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &error)
1160{
1161 if (engine)
1162 QQmlEnginePrivate::get(engine)->warning(error);
1163 else
1164 dumpwarning(error);
1165}
1166
1167void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error)
1168{
1169 if (engine)
1170 engine->warning(error);
1171 else
1172 dumpwarning(error);
1173}
1174
1175void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError> &error)
1176{
1177 if (engine)
1178 engine->warning(error);
1179 else
1180 dumpwarning(error);
1181}
1182
1183QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(
1184 const QString &fileName, const QList<QQmlJS::DiagnosticMessage> &diagnosticMessages)
1185{
1186 QList<QQmlError> errors;
1187 for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
1188 if (m.isWarning()) {
1189 qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
1190 continue;
1191 }
1192
1193 QQmlError error;
1194 error.setUrl(QUrl(fileName));
1195 error.setDescription(m.message);
1196 error.setLine(qmlConvertSourceCoordinate<quint32, int>(m.loc.startLine));
1197 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(m.loc.startColumn));
1198 errors << error;
1199 }
1200 return errors;
1201}
1202
1203void QQmlEnginePrivate::cleanupScarceResources()
1204{
1205 // iterate through the list and release them all.
1206 // note that the actual SRD is owned by the JS engine,
1207 // so we cannot delete the SRD; but we can free the
1208 // memory used by the variant in the SRD.
1209 QV4::ExecutionEngine *engine = v4Engine.get();
1210 while (QV4::ExecutionEngine::ScarceResourceData *sr = engine->scarceResources.first()) {
1211 sr->data = QVariant();
1212 engine->scarceResources.remove(sr);
1213 }
1214}
1215
1216/*!
1217 Adds \a path as a directory where the engine searches for
1218 installed modules in a URL-based directory structure.
1219
1220 The \a path may be a local filesystem directory, a
1221 \l {The Qt Resource System}{Qt Resource} path (\c {:/imports}), a
1222 \l {The Qt Resource System}{Qt Resource} url (\c {qrc:/imports}) or a URL.
1223
1224 The \a path will be converted into canonical form before it
1225 is added to the import path list.
1226
1227 The newly added \a path will be first in the importPathList().
1228
1229 \b {See also} \l setImportPathList(), \l {QML Modules},
1230 and \l [QtQml] {QML Import Path}
1231*/
1232void QQmlEngine::addImportPath(const QString& path)
1233{
1234 QQmlTypeLoader::get(this)->addImportPath(path);
1235}
1236
1237/*!
1238 Returns the list of directories where the engine searches for
1239 installed modules in a URL-based directory structure.
1240
1241 For example, if \c /opt/MyApp/lib/imports is in the path, then QML that
1242 imports \c com.mycompany.Feature will cause the QQmlEngine to look
1243 in \c /opt/MyApp/lib/imports/com/mycompany/Feature/ for the components
1244 provided by that module. A \c qmldir file is required for defining the
1245 type version mapping and possibly QML extensions plugins.
1246
1247 By default, this list contains the paths mentioned in
1248 \l {QML Import Path}.
1249
1250 \sa addImportPath(), setImportPathList()
1251*/
1252QStringList QQmlEngine::importPathList() const
1253{
1254 return QQmlTypeLoader::get(this)->importPathList();
1255}
1256
1257/*!
1258 Sets \a paths as the list of directories where the engine searches for
1259 installed modules in a URL-based directory structure.
1260
1261 By default, this list contains the paths mentioned in
1262 \l {QML Import Path}.
1263
1264 \warning Calling setImportPathList does not preserve the default
1265 import paths.
1266
1267 \sa importPathList(), addImportPath()
1268 */
1269void QQmlEngine::setImportPathList(const QStringList &paths)
1270{
1271 QQmlTypeLoader::get(this)->setImportPathList(paths);
1272}
1273
1274
1275/*!
1276 Adds \a path as a directory where the engine searches for
1277 native plugins for imported modules (referenced in the \c qmldir file).
1278
1279 By default, the list contains only \c ., i.e. the engine searches
1280 in the directory of the \c qmldir file itself.
1281
1282 The newly added \a path will be first in the pluginPathList().
1283
1284 \sa setPluginPathList()
1285*/
1286void QQmlEngine::addPluginPath(const QString& path)
1287{
1288 QQmlTypeLoader::get(this)->addPluginPath(path);
1289}
1290
1291/*!
1292 Returns the list of directories where the engine searches for
1293 native plugins for imported modules (referenced in the \c qmldir file).
1294
1295 By default, the list contains only \c ., i.e. the engine searches
1296 in the directory of the \c qmldir file itself.
1297
1298 \sa addPluginPath(), setPluginPathList()
1299*/
1300QStringList QQmlEngine::pluginPathList() const
1301{
1302 return QQmlTypeLoader::get(this)->pluginPathList();
1303}
1304
1305/*!
1306 Sets the list of directories where the engine searches for
1307 native plugins for imported modules (referenced in the \c qmldir file)
1308 to \a paths.
1309
1310 By default, the list contains only \c ., i.e. the engine searches
1311 in the directory of the \c qmldir file itself.
1312
1313 \sa pluginPathList(), addPluginPath()
1314 */
1315void QQmlEngine::setPluginPathList(const QStringList &paths)
1316{
1317 QQmlTypeLoader::get(this)->setPluginPathList(paths);
1318}
1319
1320#if QT_CONFIG(library)
1321#if QT_DEPRECATED_SINCE(6, 4)
1322/*!
1323 \deprecated [6.4] Import the module from QML with an "import" statement instead.
1324
1325 Imports the plugin named \a filePath with the \a uri provided.
1326 Returns true if the plugin was successfully imported; otherwise returns false.
1327
1328 On failure and if non-null, the \a errors list will have any errors which occurred prepended to it.
1329
1330 The plugin has to be a Qt plugin which implements the QQmlEngineExtensionPlugin interface.
1331
1332 \note Directly loading plugins like this can confuse the module import logic. In order to make
1333 the import logic load plugins from a specific place, you can use \l addPluginPath(). Each
1334 plugin should be part of a QML module that you can import using the "import" statement.
1335*/
1336bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
1337{
1338 QQmlTypeLoaderQmldirContent qmldir;
1339 QQmlPluginImporter importer(uri, QTypeRevision(), &qmldir, QQmlTypeLoader::get(this), errors);
1340 return importer.importDynamicPlugin(filePath, uri, false).isValid();
1341}
1342#endif
1343#endif
1344
1345/*!
1346 \property QQmlEngine::offlineStoragePath
1347 \brief the directory for storing offline user data
1348
1349 Returns the directory where SQL and other offline
1350 storage is placed.
1351
1352 The SQL databases created with \c openDatabaseSync() are stored here.
1353
1354 The default is QML/OfflineStorage in the platform-standard
1355 user application data directory.
1356
1357 Note that the path may not currently exist on the filesystem, so
1358 callers wanting to \e create new files at this location should create
1359 it first - see QDir::mkpath().
1360
1361 \sa {Qt Quick Local Storage QML Types}
1362*/
1363
1364/*!
1365 \fn void QQmlEngine::offlineStoragePathChanged()
1366 This signal is emitted when \l offlineStoragePath changes.
1367 \since 6.5
1368*/
1369
1370void QQmlEngine::setOfflineStoragePath(const QString& dir)
1371{
1372 Q_D(QQmlEngine);
1373 if (dir == d->offlineStoragePath)
1374 return;
1375 d->offlineStoragePath = dir;
1376 Q_EMIT offlineStoragePathChanged();
1377}
1378
1379QString QQmlEngine::offlineStoragePath() const
1380{
1381 Q_D(const QQmlEngine);
1382
1383 if (d->offlineStoragePath.isEmpty()) {
1384 QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
1385 QQmlEnginePrivate *e = const_cast<QQmlEnginePrivate *>(d);
1386 if (!dataLocation.isEmpty()) {
1387 e->offlineStoragePath = dataLocation.replace(QLatin1Char('/'), QDir::separator())
1388 + QDir::separator() + QLatin1String("QML")
1389 + QDir::separator() + QLatin1String("OfflineStorage");
1390 Q_EMIT e->q_func()->offlineStoragePathChanged();
1391 }
1392 }
1393
1394 return d->offlineStoragePath;
1395}
1396
1397/*!
1398 Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage}
1399 database with the identifier \a databaseName is (or would be) located.
1400
1401 \sa {openDatabaseSync}{LocalStorage.openDatabaseSync()}
1402 \since 5.9
1403*/
1404QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const
1405{
1406 Q_D(const QQmlEngine);
1407 QCryptographicHash md5(QCryptographicHash::Md5);
1408 md5.addData(databaseName.toUtf8());
1409 return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex());
1410}
1411
1412QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const
1413{
1414 Q_Q(const QQmlEngine);
1415 return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator();
1416}
1417
1418static bool hasRequiredProperties(const QQmlPropertyCache::ConstPtr &propertyCache)
1419{
1420 bool requiredPropertiesFound = false;
1421 // we don't expect to find any, so the loop has no early termination check
1422 if (propertyCache) {
1423 for (int idx = 0, count = propertyCache->propertyCount(); idx < count; ++idx)
1424 requiredPropertiesFound |= propertyCache->property(idx)->isRequired();
1425 }
1426 return requiredPropertiesFound;
1427}
1428
1429template<>
1430QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
1431{
1432 Q_Q(QQmlEngine);
1433
1434 QQmlType::SingletonInstanceInfo::ConstPtr siinfo = type.singletonInstanceInfo();
1435 Q_ASSERT(siinfo != nullptr);
1436
1437 QJSValue value = singletonInstances.value(siinfo);
1438 if (!value.isUndefined())
1439 return value;
1440
1441 if (siinfo->scriptCallback) {
1442 value = siinfo->scriptCallback(q, q);
1443 if (value.isQObject()) {
1444 QObject *o = value.toQObject();
1445 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
1446 // should behave identically to QML singleton types.
1447 q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
1448 }
1449 singletonInstances.convertAndInsert(v4Engine.get(), siinfo, &value);
1450
1451 } else if (siinfo->qobjectCallback) {
1452 QObject *o = siinfo->qobjectCallback(q, q);
1453 if (!o) {
1454 QQmlError error;
1455 error.setMessageType(QtMsgType::QtCriticalMsg);
1456 error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.",
1457 qPrintable(QString::fromUtf8(type.typeName()))));
1458 warning(error);
1459 } else {
1460 type.createProxy(o);
1461
1462 // if this object can use a property cache, create it now
1463 QQmlPropertyCache::ConstPtr propertyCache = QQmlData::ensurePropertyCache(o);
1464 if (Q_UNLIKELY(hasRequiredProperties(propertyCache))) {
1465 // there's no way to set required properties on a singleton
1466 delete o;
1467 o = nullptr;
1468 QQmlError error;
1469 error.setMessageType(QtMsgType::QtCriticalMsg);
1470 error.setDescription(QString::asprintf("Singleton \"%s\" is not available because the type has unset required properties.",
1471 qPrintable(QString::fromUtf8(type.typeName()))));
1472 warning(error);
1473 } else {
1474 // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
1475 // should behave identically to QML singleton types. You can, however, manually
1476 // assign a context; and clearSingletons() retains the contexts, in which case
1477 // we don't want to see warnings about the object already having a context.
1478 QQmlData *data = QQmlData::get(o, true);
1479 if (!data->context) {
1480 auto contextData = QQmlContextData::get(new QQmlContext(q->rootContext(), q));
1481 data->context = contextData.data();
1482 contextData->addOwnedObject(data);
1483 }
1484 }
1485 }
1486
1487 value = q->newQObject(o);
1488 singletonInstances.convertAndInsert(v4Engine.get(), siinfo, &value);
1489 } else if (!siinfo->url.isEmpty()) {
1490 QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
1491 if (component.isError()) {
1492 warning(component.errors());
1493 v4Engine->throwError(
1494 QLatin1String("Due to the preceding error(s), "
1495 "Singleton \"%1\" could not be loaded.")
1496 .arg(QString::fromUtf8(type.typeName())));
1497
1498 return QJSValue(QJSValue::UndefinedValue);
1499 }
1500 QObject *o = component.beginCreate(q->rootContext());
1501 auto *compPriv = QQmlComponentPrivate::get(&component);
1502 if (compPriv->hasUnsetRequiredProperties()) {
1503 /* We would only get the errors from the component after (complete)Create.
1504 We can't call create, as we need to convertAndInsert before completeCreate (otherwise
1505 tst_qqmllanguage::compositeSingletonCircular fails).
1506 On the other hand, we don't want to call cnovertAndInsert if we have an error
1507 So create the unset required component errors manually.
1508 */
1509 delete o;
1510 const auto requiredProperties = compPriv->requiredProperties();
1511 QList<QQmlError> errors (requiredProperties->size());
1512 for (const auto &reqProp: *requiredProperties)
1513 errors.push_back(QQmlComponentPrivate::unsetRequiredPropertyToQQmlError(reqProp));
1514 warning(errors);
1515 v4Engine->throwError(
1516 QLatin1String("Due to the preceding error(s), "
1517 "Singleton \"%1\" could not be loaded.")
1518 .arg(QString::fromUtf8(type.typeName())));
1519 return QJSValue(QJSValue::UndefinedValue);
1520 }
1521
1522 value = q->newQObject(o);
1523 singletonInstances.convertAndInsert(v4Engine.get(), siinfo, &value);
1524 component.completeCreate();
1525 }
1526
1527 return value;
1528}
1529
1530void QQmlEnginePrivate::executeRuntimeFunction(const QUrl &url, qsizetype functionIndex,
1531 QObject *thisObject, int argc, void **args,
1532 QMetaType *types)
1533{
1534 const auto unit = compilationUnitFromUrl(url);
1535 if (!unit)
1536 return;
1537 executeRuntimeFunction(unit, functionIndex, thisObject, argc, args, types);
1538}
1539
1540void QQmlEnginePrivate::executeRuntimeFunction(const QV4::ExecutableCompilationUnit *unit,
1541 qsizetype functionIndex, QObject *thisObject,
1542 int argc, void **args, QMetaType *types)
1543{
1544 Q_ASSERT(unit);
1545 Q_ASSERT((functionIndex >= 0) && (functionIndex < unit->runtimeFunctions.size()));
1546 Q_ASSERT(thisObject);
1547
1548 QQmlData *ddata = QQmlData::get(thisObject);
1549 Q_ASSERT(ddata && ddata->context);
1550
1551 QV4::Function *function = unit->runtimeFunctions[functionIndex];
1552 Q_ASSERT(function);
1553 Q_ASSERT(function->compiledFunction);
1554
1555 QV4::ExecutionEngine *v4 = v4Engine.get();
1556
1557 // NB: always use scriptContext() by default as this method ignores whether
1558 // there's already a stack frame (except when dealing with closures). the
1559 // method is called from C++ (through QQmlEngine::executeRuntimeFunction())
1560 // and thus the caller must ensure correct setup
1561 QV4::Scope scope(v4);
1562 QV4::ExecutionContext *ctx = v4->scriptContext();
1563 QV4::Scoped<QV4::ExecutionContext> callContext(scope,
1564 QV4::QmlContext::create(ctx, ddata->context, thisObject));
1565
1566 if (auto nested = function->nestedFunction()) {
1567 // if a nested function is already known, call the closure directly
1568 function = nested;
1569 } else if (function->isClosureWrapper()) {
1570 // if there is a nested function, but we don't know it, we need to call
1571 // an outer function first and then the inner function. we fetch the
1572 // return value of a function call (that is a closure) by calling a
1573 // different version of ExecutionEngine::callInContext() that returns a
1574 // QV4::ReturnedValue with no arguments since they are not needed by the
1575 // outer function anyhow
1576 QV4::Scoped<QV4::JavaScriptFunctionObject> result(scope,
1577 v4->callInContext(function, thisObject, callContext, 0, nullptr));
1578 Q_ASSERT(result->function());
1579 Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
1580
1581 // overwrite the function and its context
1582 function = result->function();
1583 callContext = QV4::Scoped<QV4::ExecutionContext>(scope, result->scope());
1584 }
1585
1586 v4->callInContext(function, thisObject, callContext, argc, args, types);
1587}
1588
1589QV4::ExecutableCompilationUnit *QQmlEnginePrivate::compilationUnitFromUrl(const QUrl &url)
1590{
1591 QV4::ExecutionEngine *v4 = v4Engine.get();
1592 if (auto unit = v4->compilationUnitForUrl(url)) {
1593 if (!unit->runtimeStrings)
1594 unit->populate();
1595 return unit.data();
1596 }
1597
1598 auto unit = v4->typeLoader()->getType(url)->compilationUnit();
1599 if (!unit)
1600 return nullptr;
1601
1602 auto executable = v4->executableCompilationUnit(std::move(unit));
1603 executable->populate();
1604 return executable.data();
1605}
1606
1607QQmlRefPointer<QQmlContextData>
1608QQmlEnginePrivate::createInternalContext(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
1609 const QQmlRefPointer<QQmlContextData> &parentContext,
1610 int subComponentIndex, bool isComponentRoot)
1611{
1612 Q_ASSERT(unit);
1613
1614 QQmlRefPointer<QQmlContextData> context;
1615 context = QQmlContextData::createRefCounted(parentContext);
1616 context->setInternal(true);
1617 context->setImports(unit->typeNameCache());
1618 context->initFromTypeCompilationUnit(unit, subComponentIndex);
1619
1620 const auto *dependentScripts = unit->dependentScriptsPtr();
1621 const qsizetype dependentScriptsSize = dependentScripts->size();
1622 if (isComponentRoot && dependentScriptsSize) {
1623 QV4::ExecutionEngine *v4 = v4Engine.get();
1624 Q_ASSERT(v4);
1625 QV4::Scope scope(v4);
1626
1627 QV4::ScopedObject scripts(scope, v4->newArrayObject(dependentScriptsSize));
1628 context->setImportedScripts(v4, scripts);
1629 QV4::ScopedValue v(scope);
1630 for (qsizetype i = 0; i < dependentScriptsSize; ++i)
1631 scripts->put(i, (v = dependentScripts->at(i)->scriptValueForContext(context)));
1632 }
1633
1634 return context;
1635}
1636
1637/*!
1638 \fn QQmlEngine *qmlEngine(const QObject *object)
1639 \relates QQmlEngine
1640
1641 Returns the QQmlEngine associated with \a object, if any. This is equivalent to
1642 QQmlEngine::contextForObject(object)->engine(), but more efficient.
1643
1644 \note Add \c{#include <QtQml>} to use this function.
1645
1646 \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlContext()
1647*/
1648
1649/*!
1650 \fn QQmlContext *qmlContext(const QObject *object)
1651 \relates QQmlEngine
1652
1653 Returns the QQmlContext associated with \a object, if any. This is equivalent to
1654 QQmlEngine::contextForObject(object).
1655
1656 \note Add \c{#include <QtQml>} to use this function.
1657
1658 \sa {QQmlEngine::contextForObject()}{contextForObject()}, qmlEngine()
1659*/
1660
1661void hasJsOwnershipIndicator(QQmlGuardImpl *) {};
1662
1663LoadHelper::LoadHelper(
1664 QQmlTypeLoader *loader, QAnyStringView uri, QAnyStringView typeName,
1665 QQmlTypeLoader::Mode mode)
1667 , m_uri(uri.toString())
1669 , m_mode(mode)
1670{
1671 m_typeLoader->loadWithStaticData(this, QByteArray(), m_mode);
1672}
1673
1674void LoadHelper::registerCallback(QQmlComponentPrivate *callback)
1675{
1676 m_callback = callback;
1677}
1678
1679void LoadHelper::unregisterCallback(QQmlComponentPrivate *callback)
1680{
1681 if (m_callback) {
1682 Q_ASSERT(callback == m_callback);
1683 m_callback = nullptr;
1684 }
1685}
1686
1687void LoadHelper::done()
1688{
1689 if (!couldFindModule()) {
1690 m_resolveTypeResult = ResolveTypeResult::NoSuchModule;
1691 return;
1692 }
1693
1694 QQmlTypeModule *module = QQmlMetaType::typeModule(m_uri, QTypeRevision{});
1695 if (module) {
1696 m_type = module->type(m_typeName, {});
1697 if (m_type.isValid()) {
1698 m_resolveTypeResult = ResolveTypeResult::ModuleFound;
1699 return;
1700 }
1701 }
1702
1703 // The module exists (see check above), but there is no QQmlTypeModule
1704 // ==> pure QML module, attempt resolveType
1705 QTypeRevision versionReturn;
1706 QList<QQmlError> errors;
1707 QQmlImportNamespace *ns_return = nullptr;
1708 m_importCache->resolveType(
1709 typeLoader(), m_typeName, &m_type, &versionReturn, &ns_return, &errors);
1710 m_resolveTypeResult = ResolveTypeResult::ModuleFound;
1711}
1712
1713void LoadHelper::completed()
1714{
1715 QQmlTypeLoader::Blob::completed();
1716
1717 if (m_callback) {
1718 m_callback->completeLoadFromModule(m_uri, m_typeName);
1719 m_callback = nullptr;
1720 }
1721}
1722
1723void LoadHelper::dataReceived(const SourceCodeData &)
1724{
1725 auto import = std::make_shared<PendingImport>();
1726 import->uri = m_uri;
1727 QList<QQmlError> errorList;
1728 if (!Blob::addImport(import, &errorList)) {
1729 qCDebug(lcQmlImport) << "LoadHelper: Errors loading " << m_uri << errorList;
1730 m_uri.clear(); // reset m_uri to remember the failure
1731 }
1732}
1733
1734bool LoadHelper::couldFindModule() const
1735{
1736 if (m_uri.isEmpty())
1737 return false;
1738 for (const auto &import: std::as_const(m_unresolvedImports))
1739 if (import->priority == 0) // compare QQmlTypeData::allDependenciesDone
1740 return false;
1741 return true;
1742}
1743
1744QT_END_NAMESPACE
1745
1746#include "moc_qqmlengine.cpp"
Combined button and popup list for selecting options.
static bool hasRequiredProperties(const QQmlPropertyCache::ConstPtr &propertyCache)
static void dumpwarning(const QQmlError &error)
void hasJsOwnershipIndicator(QQmlGuardImpl *)
void qml_register_types_QML()
int qmlRegisterType< void >(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
LoadHelper(QQmlTypeLoader *loader, QAnyStringView uri, QAnyStringView typeName, QQmlTypeLoader::Mode mode)
void dataReceived(const SourceCodeData &) final
Invoked when data for the blob is received.
void completed() final
Invoked on the main thread sometime after done() was called on the load thread.
void registerCallback(QQmlComponentPrivate *callback)
void unregisterCallback(QQmlComponentPrivate *callback)
void done() final
Invoked once data has either been received or a network error occurred, and all dependencies are comp...