diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index ef5bd7abb9..678e698033 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -111,10 +111,11 @@ void DesktopScriptingInterface::show(const QString& path, const QString& title) InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString& sourceUrl, const QVariantMap& properties) { if (QThread::currentThread() != thread()) { InteractiveWindowPointer interactiveWindow = nullptr; - BLOCKING_INVOKE_METHOD(this, "createWindow", + BLOCKING_INVOKE_METHOD(this, "createWindowOnThread", Q_RETURN_ARG(InteractiveWindowPointer, interactiveWindow), Q_ARG(QString, sourceUrl), - Q_ARG(QVariantMap, properties)); + Q_ARG(QVariantMap, properties), + Q_ARG(QThread*, QThread::currentThread())); return interactiveWindow; } @@ -129,3 +130,17 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString& return new InteractiveWindow(sourceUrl, properties); } + +InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread) { + + // The offscreen surface already validates against non-local QML sources, but we also need to ensure that + // if we create top level QML, like dock widgets or other types of QQuickView containing desktop windows + // that the source URL is permitted + const auto& urlValidator = OffscreenQmlSurface::getUrlValidator(); + if (!urlValidator(sourceUrl)) { + return nullptr; + } + InteractiveWindowPointer window = new InteractiveWindow(sourceUrl, properties); + window->moveToThread(targetThread); + return window; +} diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h index b4ff159176..525fd7c803 100644 --- a/interface/src/scripting/DesktopScriptingInterface.h +++ b/interface/src/scripting/DesktopScriptingInterface.h @@ -101,6 +101,8 @@ private: static int flagAlwaysOnTop() { return AlwaysOnTop; } static int flagCloseButtonHides() { return CloseButtonHides; } + Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread); + static QVariantMap getDockArea(); Q_INVOKABLE static QVariantMap getPresentationMode(); diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index dfef16b536..c85d7bc301 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -85,13 +85,13 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) { * @property {string} [title="InteractiveWindow] - The title of the window. * @property {Vec2} [position] - The initial position of the window, in pixels. * @property {Vec2} [size] - The initial size of the window, in pixels - * @property {boolean} [visible=true] - true to make the window visible when created, false to make + * @property {boolean} [visible=true] - true to make the window visible when created, false to make * it invisible. - * @property {InteractiveWindow.PresentationMode} [presentationMode=Desktop.PresentationMode.VIRTUAL] - - * Desktop.PresentationMode.VIRTUAL to display the window inside Interface, .NATIVE to display it + * @property {InteractiveWindow.PresentationMode} [presentationMode=Desktop.PresentationMode.VIRTUAL] - + * Desktop.PresentationMode.VIRTUAL to display the window inside Interface, .NATIVE to display it * as its own separate window. - * @property {InteractiveWindow.PresentationWindowInfo} [presentationWindowInfo] - Controls how a NATIVE window is - * displayed. If used, the window is docked to the specified edge of the Interface window, otherwise the window is + * @property {InteractiveWindow.PresentationWindowInfo} [presentationWindowInfo] - Controls how a NATIVE window is + * displayed. If used, the window is docked to the specified edge of the Interface window, otherwise the window is * displayed as its own separate window. * @property {InteractiveWindow.AdditionalFlags} [additionalFlags=0] - Window behavior flags in addition to "native window flags" (minimize/maximize/close), * set at window creation. Possible flag values are provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}. @@ -124,7 +124,7 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap auto mainWindow = qApp->getWindow(); _dockWidget = std::shared_ptr(new DockWidget(title, mainWindow), dockWidgetDeleter); auto quickView = _dockWidget->getQuickView(); - + Application::setupQmlSurface(quickView->rootContext() , true); //add any whitelisted callbacks @@ -176,7 +176,6 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap }); _dockWidget->setSource(QUrl(sourceUrl)); - mainWindow->addDockWidget(dockArea, _dockWidget.get()); } else { auto offscreenUi = DependencyManager::get(); @@ -254,55 +253,35 @@ void InteractiveWindow::sendToQml(const QVariant& message) { } void InteractiveWindow::emitScriptEvent(const QVariant& scriptMessage) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, scriptMessage)); - } else { - emit scriptEventReceived(scriptMessage); - } + emit scriptEventReceived(scriptMessage); } void InteractiveWindow::emitWebEvent(const QVariant& webMessage) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, webMessage)); - } else { - emit webEventReceived(webMessage); - } + emit webEventReceived(webMessage); } void InteractiveWindow::close() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "close"); - return; - } - if (_qmlWindow) { _qmlWindow->deleteLater(); } - qApp->getWindow()->removeDockWidget(_dockWidget.get()); + if (_dockWidget) { + auto window = qApp->getWindow(); + BLOCKING_INVOKE_METHOD(window, "removeDockWidget", Q_ARG(QDockWidget*, _dockWidget.get())); + } _dockWidget = nullptr; _qmlWindow = nullptr; } void InteractiveWindow::show() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "show"); - return; - } - if (_qmlWindow) { - QMetaObject::invokeMethod(_qmlWindow, "show", Qt::DirectConnection); + QMetaObject::invokeMethod(_qmlWindow, "show"); } } void InteractiveWindow::raise() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "raise"); - return; - } - if (_qmlWindow) { - QMetaObject::invokeMethod(_qmlWindow, "raiseWindow", Qt::DirectConnection); + QMetaObject::invokeMethod(_qmlWindow, "raiseWindow"); } } @@ -323,17 +302,12 @@ void InteractiveWindow::setVisible(bool visible) { } if (!_qmlWindow.isNull()) { - _qmlWindow->setProperty(INTERACTIVE_WINDOW_VISIBLE_PROPERTY, visible); + QMetaObject::invokeMethod(_qmlWindow, "setProperty", Q_ARG(const char*, INTERACTIVE_WINDOW_VISIBLE_PROPERTY), + Q_ARG(bool, visible)); } } bool InteractiveWindow::isVisible() const { - if (QThread::currentThread() != thread()) { - bool result = false; - BLOCKING_INVOKE_METHOD(const_cast(this), "isVisible", Q_RETURN_ARG(bool, result)); - return result; - } - if (_qmlWindow.isNull()) { return false; } @@ -342,12 +316,6 @@ bool InteractiveWindow::isVisible() const { } glm::vec2 InteractiveWindow::getPosition() const { - if (QThread::currentThread() != thread()) { - glm::vec2 result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getPosition", Q_RETURN_ARG(glm::vec2, result)); - return result; - } - if (_qmlWindow.isNull()) { return {}; } @@ -362,18 +330,13 @@ void InteractiveWindow::setPosition(const glm::vec2& position) { } if (!_qmlWindow.isNull()) { - _qmlWindow->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y)); - QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowPositionForMode", Qt::DirectConnection); + QMetaObject::invokeMethod(_qmlWindow, "setProperty", Q_ARG(const char*, INTERACTIVE_WINDOW_POSITION_PROPERTY), + Q_ARG(QPointF, QPointF(position.x, position.y))); + QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowPositionForMode"); } } glm::vec2 InteractiveWindow::getSize() const { - if (QThread::currentThread() != thread()) { - glm::vec2 result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getSize", Q_RETURN_ARG(glm::vec2, result)); - return result; - } - if (_qmlWindow.isNull()) { return {}; } @@ -381,24 +344,14 @@ glm::vec2 InteractiveWindow::getSize() const { } void InteractiveWindow::setSize(const glm::vec2& size) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setSize", Q_ARG(const glm::vec2&, size)); - return; - } - if (!_qmlWindow.isNull()) { - _qmlWindow->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y)); - QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowSizeForMode", Qt::DirectConnection); + QMetaObject::invokeMethod(_qmlWindow, "setProperty", Q_ARG(const char*, INTERACTIVE_WINDOW_SIZE_PROPERTY), + Q_ARG(QSize, QSize(size.x, size.y))); + QMetaObject::invokeMethod(_qmlWindow, "updateInteractiveWindowSizeForMode"); } } QString InteractiveWindow::getTitle() const { - if (QThread::currentThread() != thread()) { - QString result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getTitle", Q_RETURN_ARG(QString, result)); - return result; - } - if (_qmlWindow.isNull()) { return QString(); } @@ -406,24 +359,13 @@ QString InteractiveWindow::getTitle() const { } void InteractiveWindow::setTitle(const QString& title) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setTitle", Q_ARG(const QString&, title)); - return; - } - if (!_qmlWindow.isNull()) { - _qmlWindow->setProperty(TITLE_PROPERTY, title); + QMetaObject::invokeMethod(_qmlWindow, "setProperty", Q_ARG(const char*, TITLE_PROPERTY), + Q_ARG(QString, title)); } } int InteractiveWindow::getPresentationMode() const { - if (QThread::currentThread() != thread()) { - int result; - BLOCKING_INVOKE_METHOD(const_cast(this), "getPresentationMode", - Q_RETURN_ARG(int, result)); - return result; - } - if (_qmlWindow.isNull()) { return Virtual; } @@ -449,12 +391,8 @@ void InteractiveWindow::parentNativeWindowToMainWindow() { } void InteractiveWindow::setPresentationMode(int presentationMode) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setPresentationMode", Q_ARG(int, presentationMode)); - return; - } - if (!_qmlWindow.isNull()) { - _qmlWindow->setProperty(PRESENTATION_MODE_PROPERTY, presentationMode); + QMetaObject::invokeMethod(_qmlWindow, "setProperty", Q_ARG(const char*, PRESENTATION_MODE_PROPERTY), + Q_ARG(int, presentationMode)); } }