diff options
Diffstat (limited to 'src/qml/qml/qqmlcontextwrapper.cpp')
-rw-r--r-- | src/qml/qml/qqmlcontextwrapper.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp new file mode 100644 index 0000000000..6a3fd154d3 --- /dev/null +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlcontextwrapper_p.h" +#include <private/qv8engine_p.h> + +#include <private/qqmlengine_p.h> +#include <private/qqmlcontext_p.h> + +#include <private/qv4engine_p.h> +#include <private/qv4value_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4objectproto_p.h> +#include <private/qv4mm_p.h> +#include <private/qqmltypewrapper_p.h> +#include <private/qqmllistwrapper_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(QmlContextWrapper); + +QmlContextWrapper::QmlContextWrapper(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject, bool ownsContext) + : Object(QV8Engine::getV4(engine)), + v8(engine), readOnly(true), ownsContext(ownsContext), isNullWrapper(false), + context(context), scopeObject(scopeObject) +{ + vtbl = &static_vtbl; +} + +QmlContextWrapper::~QmlContextWrapper() +{ + if (context && ownsContext) + context->destroy(); +} + +QV4::Value QmlContextWrapper::qmlScope(QV8Engine *v8, QQmlContextData *ctxt, QObject *scope) +{ + ExecutionEngine *v4 = QV8Engine::getV4(v8); + + QmlContextWrapper *w = new (v4->memoryManager) QmlContextWrapper(v8, ctxt, scope); + w->prototype = v4->objectPrototype; + return Value::fromObject(w); +} + +QV4::Value QmlContextWrapper::urlScope(QV8Engine *v8, const QUrl &url) +{ + ExecutionEngine *v4 = QV8Engine::getV4(v8); + + QQmlContextData *context = new QQmlContextData; + context->url = url; + context->isInternal = true; + context->isJSContext = true; + + QmlContextWrapper *w = new (v4->memoryManager) QmlContextWrapper(v8, context, 0); + w->isNullWrapper = true; + w->prototype = v4->objectPrototype; + return Value::fromObject(w); +} + +QQmlContextData *QmlContextWrapper::callingContext(ExecutionEngine *v4) +{ + QV4::Object *qmlglobal = v4->qmlContextObject(); + if (!qmlglobal) + return 0; + + QmlContextWrapper *c = qmlglobal->as<QmlContextWrapper>(); + return c ? c->getContext() : 0; +} + +QQmlContextData *QmlContextWrapper::getContext(const Value &value) +{ + Object *o = value.asObject(); + QmlContextWrapper *c = o ? o->as<QmlContextWrapper>() : 0; + if (!c) + return 0; + + return c ? c->getContext():0; +} + +void QmlContextWrapper::takeContextOwnership(const Value &qmlglobal) +{ + Object *o = qmlglobal.asObject(); + QmlContextWrapper *c = o ? o->as<QmlContextWrapper>() : 0; + assert(c); + c->ownsContext = true; +} + + +Value QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty) +{ + QmlContextWrapper *resource = m->as<QmlContextWrapper>(); + QV4::ExecutionEngine *v4 = m->engine(); + if (!resource) + v4->current->throwTypeError(); + + if (resource->isNullWrapper) + return Object::get(m, name, hasProperty); + + if (QV4::QmlContextWrapper::callingContext(v4) != resource->context) + return Object::get(m, name, hasProperty); + + bool hasProp; + Value result = Object::get(m, name, &hasProp); + if (hasProp) { + if (hasProperty) + *hasProperty = hasProp; + return result; + } + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) { + if (hasProperty) + *hasProperty = true; + return result; + } + + // Search type (attached property/enum/imported scripts) names + // while (context) { + // Search context properties + // Search scope object + // Search context object + // context = context->parent + // } + + QV8Engine *engine = resource->v8; + + QObject *scopeObject = resource->getScopeObject(); + + QHashedV4String propertystring(Value::fromString(name)); + + if (context->imports && name->startsWithUpper()) { + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = context->imports->query(propertystring); + + if (r.isValid()) { + if (hasProperty) + *hasProperty = true; + if (r.scriptIndex != -1) { + int index = r.scriptIndex; + if (index < context->importedScripts.count()) + return context->importedScripts.at(index).value(); + else + return QV4::Value::undefinedValue(); + } else if (r.type) { + return QmlTypeWrapper::create(engine, scopeObject, r.type); + } else if (r.importNamespace) { + return QmlTypeWrapper::create(engine, scopeObject, context->imports, r.importNamespace); + } + Q_ASSERT(!"Unreachable"); + } + + // Fall through + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + + while (context) { + // Search context properties + if (context->propertyNames) { + int propertyIdx = context->propertyNames->value(propertystring); + + if (propertyIdx != -1) { + + if (propertyIdx < context->idValueCount) { + + ep->captureProperty(&context->idValues[propertyIdx].bindings); + if (hasProperty) + *hasProperty = true; + return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]); + } else { + + QQmlContextPrivate *cp = context->asQQmlContextPrivate(); + + ep->captureProperty(context->asQQmlContext(), -1, + propertyIdx + cp->notifyIndex); + + const QVariant &value = cp->propertyValues.at(propertyIdx); + if (hasProperty) + *hasProperty = true; + if (value.userType() == qMetaTypeId<QList<QObject*> >()) { + QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx), + QQmlContextPrivate::context_count, + QQmlContextPrivate::context_at); + return QmlListWrapper::create(engine, prop, qMetaTypeId<QQmlListProperty<QObject> >()); + } else { + return engine->fromVariant(cp->propertyValues.at(propertyIdx)); + } + } + } + } + + // Search scope object + if (scopeObject) { + bool hasProp = false; + QV4::Value result = QV4::QObjectWrapper::getQmlProperty(v4->current, context, scopeObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp); + if (hasProp) { + if (hasProperty) + *hasProperty = true; + return result; + } + } + scopeObject = 0; + + + // Search context object + if (context->contextObject) { + bool hasProp = false; + QV4::Value result = QV4::QObjectWrapper::getQmlProperty(v4->current, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp); + if (hasProp) { + if (hasProperty) + *hasProperty = true; + return result; + } + } + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + return Value::undefinedValue(); +} + +void QmlContextWrapper::put(Managed *m, String *name, const Value &value) +{ + QmlContextWrapper *wrapper = m->as<QmlContextWrapper>(); + ExecutionEngine *v4 = m->engine(); + if (!wrapper) + v4->current->throwTypeError(); + + if (wrapper->isNullWrapper) { + if (wrapper && wrapper->readOnly) { + QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + + QLatin1Char('"'); + v4->current->throwError(Value::fromString(v4->current->engine->newString(error))); + } + + Object::put(m, name, value); + return; + } + + PropertyAttributes attrs; + Property *pd = wrapper->__getOwnProperty__(name, &attrs); + if (pd) { + wrapper->putValue(pd, attrs, value); + return; + } + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = wrapper->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return; + + // See QV8ContextWrapper::Getter for resolution order + + QObject *scopeObject = wrapper->getScopeObject(); + + QHashedV4String propertystring(Value::fromString(name)); + + while (context) { + // Search context properties + if (context->propertyNames && -1 != context->propertyNames->value(propertystring)) + return; + + // Search scope object + if (scopeObject && + QV4::QObjectWrapper::setQmlProperty(v4->current, context, scopeObject, name, QV4::QObjectWrapper::CheckRevision, value)) + return; + scopeObject = 0; + + // Search context object + if (context->contextObject && + QV4::QObjectWrapper::setQmlProperty(v4->current, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, value)) + return; + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + if (wrapper->readOnly) { + QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + + QLatin1Char('"'); + v4->current->throwError(error); + } + + Object::put(m, name, value); +} + +void QmlContextWrapper::destroy(Managed *that) +{ + static_cast<QmlContextWrapper *>(that)->~QmlContextWrapper(); +} + +QT_END_NAMESPACE |