mirror of
https://github.com/overte-org/overte.git
synced 2025-04-15 14:56:57 +02:00
Working on QML whitelist functionality
This commit is contained in:
parent
a5caae3739
commit
ffc51d5387
7 changed files with 113 additions and 62 deletions
18
interface/resources/qml/OverlayWindowTest.qml
Normal file
18
interface/resources/qml/OverlayWindowTest.qml
Normal file
|
@ -0,0 +1,18 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 100
|
||||
color: "white"
|
||||
Rectangle {
|
||||
width: 10
|
||||
height: 10
|
||||
color: "red"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: OverlayWindowTestString
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@ Windows.Window {
|
|||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
property var source;
|
||||
property var component;
|
||||
property var dynamicContent;
|
||||
|
||||
// Keyboard control properties in case needed by QML content.
|
||||
|
@ -35,28 +34,13 @@ Windows.Window {
|
|||
dynamicContent.destroy();
|
||||
dynamicContent = null;
|
||||
}
|
||||
component = Qt.createComponent(source);
|
||||
console.log("Created component " + component + " from source " + source);
|
||||
}
|
||||
|
||||
onComponentChanged: {
|
||||
console.log("Component changed to " + component)
|
||||
populate();
|
||||
}
|
||||
|
||||
function populate() {
|
||||
console.log("Populate called: dynamicContent " + dynamicContent + " component " + component);
|
||||
if (!dynamicContent && component) {
|
||||
if (component.status == Component.Error) {
|
||||
console.log("Error loading component:", component.errorString());
|
||||
} else if (component.status == Component.Ready) {
|
||||
console.log("Building dynamic content");
|
||||
dynamicContent = component.createObject(contentHolder);
|
||||
} else {
|
||||
console.log("Component not yet ready, connecting to status change");
|
||||
component.statusChanged.connect(populate);
|
||||
}
|
||||
}
|
||||
console.log("QQQ Foo");
|
||||
QmlSurface.createContentFromQml(source, contentHolder, function(newObject) {
|
||||
console.log("QQQ Bar " + dynamicContent);
|
||||
dynamicContent = newObject;
|
||||
dynamicContent.visible = true;
|
||||
});
|
||||
console.log("QQQ Baz");
|
||||
}
|
||||
|
||||
// Handle message traffic from the script that launched us to the loaded QML
|
||||
|
|
|
@ -2240,6 +2240,12 @@ extern void setupPreferences();
|
|||
void Application::initializeUi() {
|
||||
// Make sure all QML surfaces share the main thread GL context
|
||||
OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext());
|
||||
OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "qrc:///qml/OverlayWindowTest.qml" },
|
||||
[](QQmlContext* context) {
|
||||
qDebug() << "Whitelist OverlayWindow worked";
|
||||
context->setContextProperty("OverlayWindowTestString", "TestWorked");
|
||||
});
|
||||
|
||||
|
||||
AddressBarDialog::registerType();
|
||||
ErrorDialog::registerType();
|
||||
|
|
|
@ -62,7 +62,7 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
|
|||
|
||||
QUrl url { properties[SOURCE_PROPERTY].toString() };
|
||||
if (url.scheme() != "http" && url.scheme() != "https" && url.scheme() != "file" && url.scheme() != "about" &&
|
||||
url.scheme() != "atp") {
|
||||
url.scheme() != "atp" && url.scheme() != "qrc") {
|
||||
properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url.toString()).toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,11 +64,19 @@ public:
|
|||
for (const auto& url : urls) {
|
||||
_callbacks[url].push_back(callback);
|
||||
}
|
||||
for (const auto& url : _callbacks.keys()) {
|
||||
qDebug() << "URL found for " << url << " with " << _callbacks[url].size() << " items";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QList<QmlContextCallback> getCallbacksForUrl(const QUrl& url) const {
|
||||
qDebug() << "Looking for callbacks for " << url;
|
||||
|
||||
return resultWithReadLock<QList<QmlContextCallback>>([&] {
|
||||
for (const auto& url : _callbacks.keys()) {
|
||||
qDebug() << "URL found for " << url << " with " << _callbacks[url].size() << " items";
|
||||
}
|
||||
QList<QmlContextCallback> result;
|
||||
auto itr = _callbacks.find(url);
|
||||
if (_callbacks.end() != itr) {
|
||||
|
@ -98,7 +106,7 @@ void OffscreenQmlSurface::addWhitelistContextHandler(const std::initializer_list
|
|||
}
|
||||
|
||||
|
||||
QmlContextObjectCallback OffscreenQmlSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QObject*) {};
|
||||
QmlContextObjectCallback OffscreenQmlSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
|
||||
|
||||
struct TextureSet {
|
||||
// The number of surfaces with this size
|
||||
|
@ -590,6 +598,7 @@ void OffscreenQmlSurface::create() {
|
|||
_qmlContext->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||
_qmlContext->setContextProperty("eventBridge", this);
|
||||
_qmlContext->setContextProperty("webEntity", this);
|
||||
_qmlContext->setContextProperty("QmlSurface", this);
|
||||
|
||||
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
||||
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
||||
|
@ -688,20 +697,14 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
|
|||
_qmlContext->setBaseUrl(baseUrl);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& onQmlLoadedCallback) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qCWarning(uiLogging) << "Called load on a non-surface thread";
|
||||
}
|
||||
// Synchronous loading may take a while; restart the deadlock timer
|
||||
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
||||
|
||||
QQmlContext* OffscreenQmlSurface::contextForUrl(const QUrl& qmlSource, bool forceNewContext) {
|
||||
// Get any whitelist functionality
|
||||
QList<QmlContextCallback> callbacks = getQmlWhitelist()->getCallbacksForUrl(qmlSource);
|
||||
// If we have whitelisted content, we must load a new context
|
||||
createNewContext |= !callbacks.empty();
|
||||
forceNewContext |= !callbacks.empty();
|
||||
|
||||
QQmlContext* targetContext = _qmlContext;
|
||||
if (_rootItem && createNewContext) {
|
||||
if (_rootItem && forceNewContext) {
|
||||
targetContext = new QQmlContext(targetContext);
|
||||
}
|
||||
|
||||
|
@ -709,6 +712,15 @@ void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, con
|
|||
callback(targetContext);
|
||||
}
|
||||
|
||||
return targetContext;
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& onQmlLoadedCallback) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qCWarning(uiLogging) << "Called load on a non-surface thread";
|
||||
}
|
||||
// Synchronous loading may take a while; restart the deadlock timer
|
||||
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
||||
|
||||
// FIXME eliminate loading of relative file paths for QML
|
||||
QUrl finalQmlSource = qmlSource;
|
||||
|
@ -716,17 +728,36 @@ void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, con
|
|||
finalQmlSource = _qmlContext->resolvedUrl(qmlSource);
|
||||
}
|
||||
|
||||
auto targetContext = contextForUrl(finalQmlSource);
|
||||
auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
|
||||
if (qmlComponent->isLoading()) {
|
||||
connect(qmlComponent, &QQmlComponent::statusChanged, this, [=](QQmlComponent::Status) {
|
||||
finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback);
|
||||
finishQmlLoad(qmlComponent, targetContext, nullptr, onQmlLoadedCallback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback);
|
||||
finishQmlLoad(qmlComponent, targetContext, nullptr, onQmlLoadedCallback);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::createContentFromQml(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) {
|
||||
auto targetContext = contextForUrl(qmlSource);
|
||||
|
||||
auto onQmlLoadedCallback = [=](QQmlContext*, QObject* newItem) {
|
||||
QJSValue(callback).call(QJSValueList() << _qmlContext->engine()->newQObject(newItem));
|
||||
};
|
||||
|
||||
auto qmlComponent = new QQmlComponent(_qmlContext->engine(), qmlSource, QQmlComponent::PreferSynchronous);
|
||||
if (qmlComponent->isLoading()) {
|
||||
connect(qmlComponent, &QQmlComponent::statusChanged, this, [=](QQmlComponent::Status) {
|
||||
finishQmlLoad(qmlComponent, targetContext, parent, onQmlLoadedCallback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
finishQmlLoad(qmlComponent, targetContext, parent, onQmlLoadedCallback);
|
||||
}
|
||||
|
||||
|
||||
void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback) {
|
||||
load(qmlSource, true, onQmlLoadedCallback);
|
||||
}
|
||||
|
@ -743,7 +774,8 @@ void OffscreenQmlSurface::clearCache() {
|
|||
_qmlContext->engine()->clearComponentCache();
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, const QmlContextObjectCallback& callback) {
|
||||
|
||||
void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, QQuickItem* parent, const QmlContextObjectCallback& callback) {
|
||||
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
|
@ -765,6 +797,22 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
|
|||
return;
|
||||
}
|
||||
|
||||
if (!newObject) {
|
||||
if (!_rootItem) {
|
||||
qFatal("Could not load object as root item");
|
||||
return;
|
||||
}
|
||||
qCWarning(uiLogging) << "Unable to load QML item";
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
|
||||
if (qmlContext != _qmlContext && eventBridge && eventBridge != this) {
|
||||
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
||||
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
||||
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext));
|
||||
}
|
||||
|
||||
qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
|
||||
|
||||
// All quick items should be focusable
|
||||
|
@ -775,35 +823,26 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
|
|||
newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
|
||||
}
|
||||
|
||||
|
||||
// Make sure we will call callback for this codepath
|
||||
// Call this before qmlComponent->completeCreate() otherwise ghost window appears
|
||||
if (newItem && _rootItem) {
|
||||
callback(qmlContext, newObject);
|
||||
}
|
||||
// If we already have a root, just set a couple of flags and the ancestry
|
||||
if (_rootItem) {
|
||||
callback(qmlContext, newItem);
|
||||
|
||||
QObject* eventBridge = qmlContext->contextProperty("eventBridge").value<QObject*>();
|
||||
if (qmlContext != _qmlContext && eventBridge && eventBridge != this) {
|
||||
// FIXME Compatibility mechanism for existing HTML and JS that uses eventBridgeWrapper
|
||||
// Find a way to flag older scripts using this mechanism and wanr that this is deprecated
|
||||
qmlContext->setContextProperty("eventBridgeWrapper", new EventBridgeWrapper(eventBridge, qmlContext));
|
||||
if (!parent) {
|
||||
parent = _rootItem;
|
||||
}
|
||||
// Allow child windows to be destroyed from JS
|
||||
QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership);
|
||||
newObject->setParent(parent);
|
||||
newItem->setParentItem(parent);
|
||||
}
|
||||
|
||||
qmlComponent->completeCreate();
|
||||
qmlComponent->deleteLater();
|
||||
|
||||
// If we already have a root, just set a couple of flags and the ancestry
|
||||
if (newItem && _rootItem) {
|
||||
// Allow child windows to be destroyed from JS
|
||||
QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership);
|
||||
newObject->setParent(_rootItem);
|
||||
if (newItem) {
|
||||
newItem->setParentItem(_rootItem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!newItem) {
|
||||
qFatal("Could not load object as root item");
|
||||
if (_rootItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -815,7 +854,7 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext
|
|||
_rootItem->setSize(_quickWindow->renderTargetSize());
|
||||
|
||||
// Call this callback after rootitem is set, otherwise VrMenu wont work
|
||||
callback(qmlContext, newObject);
|
||||
callback(qmlContext, newItem);
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::updateQuick() {
|
||||
|
|
|
@ -30,13 +30,14 @@ class QQmlContext;
|
|||
class QQmlComponent;
|
||||
class QQuickWindow;
|
||||
class QQuickItem;
|
||||
class QJSValue;
|
||||
|
||||
// GPU resources are typically buffered for one copy being used by the renderer,
|
||||
// one copy in flight, and one copy being used by the receiver
|
||||
#define GPU_RESOURCE_BUFFER_SIZE 3
|
||||
|
||||
using QmlContextCallback = std::function<void(QQmlContext*)>;
|
||||
using QmlContextObjectCallback = std::function<void(QQmlContext*, QObject*)>;
|
||||
using QmlContextObjectCallback = std::function<void(QQmlContext*, QQuickItem*)>;
|
||||
|
||||
class OffscreenQmlSurface : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -57,6 +58,8 @@ public:
|
|||
void resize(const QSize& size, bool forceResize = false);
|
||||
QSize size() const;
|
||||
|
||||
Q_INVOKABLE void createContentFromQml(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
|
||||
|
||||
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK);
|
||||
|
@ -123,9 +126,10 @@ protected:
|
|||
void setFocusText(bool newFocusText);
|
||||
|
||||
private:
|
||||
QQmlContext* contextForUrl(const QUrl& url, bool forceNewContext = false);
|
||||
static QOpenGLContext* getSharedContext();
|
||||
|
||||
void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, const QmlContextObjectCallback& callbacks);
|
||||
void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, QQuickItem* parent, const QmlContextObjectCallback& callbacks);
|
||||
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
|
||||
void setupFbo();
|
||||
bool allowNewFrame(uint8_t fps);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
print("Launching web window");
|
||||
qmlWindow = new OverlayWindow({
|
||||
title: 'Test Qml',
|
||||
source: "https://s3.amazonaws.com/DreamingContent/qml/content.qml",
|
||||
source: "qrc:///qml/OverlayWindowTest.qml",
|
||||
height: 240,
|
||||
width: 320,
|
||||
toolWindow: false,
|
||||
|
|
Loading…
Reference in a new issue