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