Files
RedBear-OS/local/recipes/qt/qtdeclarative/source/src/quicktemplates/qquickstackview_p.cpp
T
vasilito f31522130f fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed
Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
2026-05-05 20:20:37 +01:00

458 lines
16 KiB
C++

// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qquickstackview_p_p.h"
#include "qquickstackelement_p_p.h"
#if QT_CONFIG(quick_viewtransitions)
#include "qquickstacktransition_p_p.h"
#endif
#include <QtCore/qscopedvaluerollback.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmllist.h>
#include <QtQml/private/qv4qmlcontext_p.h>
#include <QtQml/private/qv4qobjectwrapper_p.h>
#include <QtQml/private/qv4variantobject_p.h>
#include <QtQml/private/qv4urlobject_p.h>
#include <QtQuick/private/qquickanimation_p.h>
#include <QtQuick/private/qquicktransition_p.h>
QT_BEGIN_NAMESPACE
void QQuickStackViewPrivate::warn(const QString &error)
{
Q_Q(QQuickStackView);
if (operation.isEmpty())
qmlWarning(q) << error;
else
qmlWarning(q) << operation << ": " << error;
}
void QQuickStackViewPrivate::warnOfInterruption(const QString &attemptedOperation)
{
Q_Q(QQuickStackView);
qmlWarning(q) << "cannot " << attemptedOperation << " while already in the process of completing a " << operation;
}
void QQuickStackViewPrivate::setCurrentItem(QQuickStackElement *element)
{
Q_Q(QQuickStackView);
QQuickItem *item = element ? element->item : nullptr;
if (currentItem == item)
return;
currentItem = item;
if (element)
element->setVisible(true);
if (item)
item->setFocus(true);
emit q->currentItemChanged();
}
static bool initProperties(QQuickStackElement *element, const QV4::Value &props, QQmlV4FunctionPtr args)
{
if (props.isObject()) {
const QV4::QObjectWrapper *wrapper = props.as<QV4::QObjectWrapper>();
if (!wrapper) {
QV4::ExecutionEngine *v4 = args->v4engine();
element->properties.set(v4, props);
element->qmlCallingContext.set(v4, v4->qmlContext());
return true;
}
}
return false;
}
QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(int from, QQmlV4FunctionPtr args, QStringList *errors)
{
QV4::ExecutionEngine *v4 = args->v4engine();
auto context = v4->callingQmlContext();
QV4::Scope scope(v4);
QList<QQuickStackElement *> elements;
int argc = args->length();
for (int i = from; i < argc; ++i) {
QV4::ScopedValue arg(scope, (*args)[i]);
if (QV4::ArrayObject *array = arg->as<QV4::ArrayObject>()) {
const uint len = uint(array->getLength());
for (uint j = 0; j < len; ++j) {
QString error;
QV4::ScopedValue value(scope, array->get(j));
QQuickStackElement *element = createElement(value, context, &error);
if (element) {
if (j < len - 1) {
QV4::ScopedValue props(scope, array->get(j + 1));
if (initProperties(element, props, args))
++j;
}
elements += element;
} else if (!error.isEmpty()) {
*errors += error;
}
}
} else {
QString error;
QQuickStackElement *element = createElement(arg, context, &error);
if (element) {
if (i < argc - 1) {
QV4::ScopedValue props(scope, (*args)[i + 1]);
if (initProperties(element, props, args))
++i;
}
elements += element;
} else if (!error.isEmpty()) {
*errors += error;
}
}
}
return elements;
}
QList<QQuickStackElement *> QQuickStackViewPrivate::parseElements(
QQmlEngine *engine, const QList<QQuickStackViewArg> &args)
{
Q_Q(QQuickStackView);
QList<QQuickStackElement *> stackElements;
for (int i = 0; i < args.size(); ++i) {
const QQuickStackViewArg &arg = args.at(i);
QVariantMap properties;
// Look ahead at the next arg in case it contains properties for this
// Item/Component/URL.
if (i < args.size() - 1) {
const QQuickStackViewArg &nextArg = args.at(i + 1);
// If mProperties isn't empty, the user passed properties.
// If it is empty, but mItem, mComponent and mUrl also are,
// then they passed an empty property map.
if (!nextArg.mProperties.isEmpty()
|| (!nextArg.mItem && !nextArg.mComponent && !nextArg.mUrl.isValid())) {
properties = nextArg.mProperties;
++i;
}
}
// Remove any items that are already in the stack, as they can't be in two places at once.
if (findElement(arg.mItem))
continue;
// We look ahead one index for each Item/Component/URL, so if this arg is
// a property map, the user has passed two or more in a row.
if (!arg.mProperties.isEmpty()) {
qmlWarning(q) << "Properties must come after an Item, Component or URL";
return {};
}
QQuickStackElement *element = QQuickStackElement::fromStackViewArg(engine, q, arg);
QV4::ExecutionEngine *v4Engine = engine->handle();
element->properties.set(v4Engine, v4Engine->fromVariant(properties));
element->qmlCallingContext.set(v4Engine, v4Engine->qmlContext());
stackElements.append(element);
}
return stackElements;
}
QQuickStackElement *QQuickStackViewPrivate::findElement(QQuickItem *item) const
{
if (item) {
for (QQuickStackElement *e : std::as_const(elements)) {
if (e->item == item)
return e;
}
}
return nullptr;
}
QQuickStackElement *QQuickStackViewPrivate::findElement(const QV4::Value &value) const
{
if (const QV4::QObjectWrapper *o = value.as<QV4::QObjectWrapper>())
return findElement(qobject_cast<QQuickItem *>(o->object()));
return nullptr;
}
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer<QQmlContextData> &context)
{
if (url.isRelative())
return context->resolvedUrl(url);
return url;
}
static QString resolvedUrl(const QString &str, const QQmlRefPointer<QQmlContextData> &context)
{
QUrl url(str);
if (url.isRelative())
return context->resolvedUrl(url).toString();
return str;
}
QQuickStackElement *QQuickStackViewPrivate::createElement(
const QV4::Value &value, const QQmlRefPointer<QQmlContextData> &context, QString *error)
{
Q_Q(QQuickStackView);
if (const QV4::String *s = value.as<QV4::String>())
return QQuickStackElement::fromString(
s->engine()->qmlEngine(), resolvedUrl(s->toQString(), context), q, error);
if (const QV4::QObjectWrapper *o = value.as<QV4::QObjectWrapper>())
return QQuickStackElement::fromObject(o->object(), q, error);
if (const QV4::UrlObject *u = value.as<QV4::UrlObject>())
return QQuickStackElement::fromString(
u->engine()->qmlEngine(), resolvedUrl(u->href(), context), q, error);
if (const QV4::Object *o = value.as<QV4::Object>()) {
const QVariant data = QV4::ExecutionEngine::toVariant(value, QMetaType::fromType<QUrl>());
if (data.typeId() == QMetaType::QUrl) {
return QQuickStackElement::fromString(
o->engine()->qmlEngine(), resolvedUrl(data.toUrl(), context).toString(), q,
error);
}
}
return nullptr;
}
bool QQuickStackViewPrivate::pushElements(
QV4::ExecutionEngine *v4, const QList<QQuickStackElement *> &elems)
{
Q_Q(QQuickStackView);
if (!elems.isEmpty()) {
for (QQuickStackElement *e : elems) {
e->setIndex(elements.size());
elements += e;
}
return elements.top()->load(v4, q);
}
return false;
}
bool QQuickStackViewPrivate::pushElement(QV4::ExecutionEngine *v4, QQuickStackElement *element)
{
if (element)
return pushElements(v4, QList<QQuickStackElement *>() << element);
return false;
}
bool QQuickStackViewPrivate::popElements(QV4::ExecutionEngine *v4, QQuickStackElement *element)
{
Q_Q(QQuickStackView);
while (elements.size() > 1 && elements.top() != element) {
delete elements.pop();
if (!element)
break;
}
return elements.top()->load(v4, q);
}
bool QQuickStackViewPrivate::replaceElements(
QV4::ExecutionEngine *v4, QQuickStackElement *target,
const QList<QQuickStackElement *> &elems)
{
if (target) {
while (!elements.isEmpty()) {
QQuickStackElement* top = elements.pop();
delete top;
if (top == target)
break;
}
}
return pushElements(v4, elems);
}
QQuickItem *QQuickStackViewPrivate::popToItem(
QV4::ExecutionEngine *v4, QQuickItem *item, QQuickStackView::Operation operation,
CurrentItemPolicy currentItemPolicy)
{
const QString operationName = QStringLiteral("pop");
if (modifyingElements) {
warnOfInterruption(operationName);
return nullptr;
}
QScopedValueRollback<bool> modifyingElementsRollback(modifyingElements, true);
QScopedValueRollback<QString> operationNameRollback(this->operation, operationName);
if (elements.isEmpty()) {
warn(QStringLiteral("no items to pop"));
return nullptr;
}
if (!item) {
warn(QStringLiteral("item cannot be null"));
return nullptr;
}
const int oldDepth = elements.size();
QQuickStackElement *exit = elements.pop();
// top() here will be the item below the previously current item, since we just popped above.
QQuickStackElement *enter = elements.top();
bool nothingToDo = false;
if (item != currentItem) {
if (!item) {
// Popping down to the first item.
enter = elements.value(0);
} else {
// Popping down to an arbitrary item.
enter = findElement(item);
if (!enter) {
warn(QStringLiteral("can't find item to pop: ") + QDebug::toString(item));
nothingToDo = true;
}
}
} else {
if (currentItemPolicy == CurrentItemPolicy::DoNotPop) {
// popToItem was called with the currentItem, which means there are no items
// to pop because it's already at the top.
nothingToDo = true;
}
// else: popToItem was called by popCurrentItem, and so we _should_ pop.
}
if (nothingToDo) {
// Restore the element we popped earlier.
elements.push(exit);
return nullptr;
}
QQuickItem *previousItem = nullptr;
if (popElements(v4, enter)) {
if (exit) {
exit->removal = true;
removing.insert(exit);
previousItem = exit->item;
}
depthChange(elements.size(), oldDepth);
#if QT_CONFIG(quick_viewtransitions)
Q_Q(QQuickStackView);
startTransition(QQuickStackTransition::popExit(operation, exit, q),
QQuickStackTransition::popEnter(operation, enter, q),
operation == QQuickStackView::Immediate);
#else
Q_UNUSED(operation);
#endif
setCurrentItem(enter);
}
return previousItem;
}
#if QT_CONFIG(quick_viewtransitions)
void QQuickStackViewPrivate::ensureTransitioner()
{
if (!transitioner) {
transitioner = new QQuickItemViewTransitioner;
transitioner->setChangeListener(this);
}
}
void QQuickStackViewPrivate::startTransition(const QQuickStackTransition &first, const QQuickStackTransition &second, bool immediate)
{
if (first.element)
first.element->transitionNextReposition(transitioner, first.type, first.target);
if (second.element)
second.element->transitionNextReposition(transitioner, second.type, second.target);
if (first.element) {
// Let the check for immediate happen after prepareTransition() is
// called, because we need the prepared transition in both branches.
// Same for the second element.
if (!first.element->item || !first.element->prepareTransition(transitioner, first.viewBounds) || immediate)
completeTransition(first.element, first.transition, first.status);
else
first.element->startTransition(transitioner, first.status);
}
if (second.element) {
if (!second.element->item || !second.element->prepareTransition(transitioner, second.viewBounds) || immediate)
completeTransition(second.element, second.transition, second.status);
else
second.element->startTransition(transitioner, second.status);
}
if (transitioner) {
setBusy(!transitioner->runningJobs.isEmpty());
transitioner->resetTargetLists();
}
}
void QQuickStackViewPrivate::completeTransition(QQuickStackElement *element, QQuickTransition *transition, QQuickStackView::Status status)
{
element->setStatus(status);
if (transition) {
if (element->prepared) {
// Here we force reading all the animations, even if the desired
// transition type is StackView.Immediate. After that we force
// all the animations to complete immediately, without waiting for
// the animation timer.
// This allows us to correctly restore all the properties affected
// by the push/pop animations.
ACTION_IF_DELETED(element, element->completeTransition(transition), return);
} else if (element->item) {
// At least try to move the item to its desired place. This,
// however, is only a partly correct solution, because a lot more
// properties can be affected by the transition
element->item->setPosition(element->nextTransitionTo);
}
}
viewItemTransitionFinished(element);
}
void QQuickStackViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *transitionable)
{
QQuickStackElement *element = static_cast<QQuickStackElement *>(transitionable);
if (element->status == QQuickStackView::Activating) {
element->setStatus(QQuickStackView::Active);
} else if (element->status == QQuickStackView::Deactivating) {
element->setStatus(QQuickStackView::Inactive);
QQuickStackElement *existingElement = element->item ? findElement(element->item) : nullptr;
// If a different element with the same item is found,
// do not call setVisible(false) since it needs to be visible.
if (!existingElement || element == existingElement)
element->setVisible(false);
if (element->removal || element->isPendingRemoval())
removed += element;
}
if (transitioner && transitioner->runningJobs.isEmpty()) {
// ~QQuickStackElement() emits QQuickStackViewAttached::removed(), which may be used
// to modify the stack. Set the status first and make a copy of the destroyable stack
// elements to exclude any modifications that may happen during qDeleteAll(). (QTBUG-62153)
setBusy(false);
QList<QQuickStackElement*> removedElements = removed;
removed.clear();
for (QQuickStackElement *removedElement : std::as_const(removedElements)) {
// If an element with the same item is found in the active stack list,
// forget about the item so that we don't hide it.
if (removedElement->item && findElement(removedElement->item)) {
QQuickItemPrivate::get(removedElement->item)->removeItemChangeListener(removedElement, QQuickItemPrivate::Destroyed);
removedElement->item = nullptr;
}
}
qDeleteAll(removedElements);
}
removing.remove(element);
}
#endif
void QQuickStackViewPrivate::setBusy(bool b)
{
Q_Q(QQuickStackView);
if (busy == b)
return;
busy = b;
q->setFiltersChildMouseEvents(busy);
emit q->busyChanged();
}
void QQuickStackViewPrivate::depthChange(int newDepth, int oldDepth)
{
Q_Q(QQuickStackView);
if (newDepth == oldDepth)
return;
emit q->depthChanged();
if (newDepth == 0 || oldDepth == 0)
emit q->emptyChanged();
}
QT_END_NAMESPACE