diff --git a/interface/resources/images/high-fidelity-banner.svg b/interface/resources/images/high-fidelity-banner.svg
index d5666be0fa..19127e729c 100644
--- a/interface/resources/images/high-fidelity-banner.svg
+++ b/interface/resources/images/high-fidelity-banner.svg
@@ -1,17 +1 @@
-
-
+
\ No newline at end of file
diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml
index 32c19daf14..ff574ceaa5 100644
--- a/interface/resources/qml/Web3DSurface.qml
+++ b/interface/resources/qml/Web3DSurface.qml
@@ -12,20 +12,35 @@ import QtQuick 2.5
import "controls" as Controls
-Controls.WebView {
+Item {
+ id: root
+ anchors.fill: parent
+ property string url: ""
+ property string scriptUrl: null
- // This is for JS/QML communication, which is unused in a Web3DOverlay,
- // but not having this here results in spurious warnings about a
- // missing signal
- signal sendToScript(var message);
+ onUrlChanged: {
+ load(root.url, root.scriptUrl);
+ }
- function onWebEventReceived(event) {
- if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
- ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
+ onScriptUrlChanged: {
+ if (root.item) {
+ root.item.scriptUrl = root.scriptUrl;
+ } else {
+ load(root.url, root.scriptUrl);
}
}
+ property var item: null
+
+ function load(url, scriptUrl) {
+ QmlSurface.load("./controls/WebView.qml", root, function(newItem) {
+ root.item = newItem
+ root.item.url = url
+ root.item.scriptUrl = scriptUrl
+ })
+ }
+
Component.onCompleted: {
- eventBridge.webEventReceived.connect(onWebEventReceived);
+ load(root.url, root.scriptUrl);
}
}
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index a1d24fe52e..b1feddfd47 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include "EntitiesRendererLogging.h"
#include
@@ -180,14 +181,23 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
}
// This work must be done on the main thread
+ bool localSafeContext = entity->getLocalSafeContext();
if (!_webSurface) {
+ if (localSafeContext) {
+ ::hifi::scripting::setLocalAccessSafeThread(true);
+ }
buildWebSurface(entity, newSourceURL);
+ ::hifi::scripting::setLocalAccessSafeThread(false);
}
if (_webSurface) {
if (_webSurface->getRootItem()) {
if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) {
+ if (localSafeContext) {
+ ::hifi::scripting::setLocalAccessSafeThread(true);
+ }
_webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL);
+ ::hifi::scripting::setLocalAccessSafeThread(false);
_sourceURL = newSourceURL;
} else if (_contentType != ContentType::HtmlContent) {
_sourceURL = newSourceURL;
diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp
index 186a8fa8b4..a62f599e4c 100644
--- a/libraries/entities/src/WebEntityItem.cpp
+++ b/libraries/entities/src/WebEntityItem.cpp
@@ -15,6 +15,7 @@
#include
#include
+#include
#include "EntitiesLogging.h"
#include "EntityItemProperties.h"
@@ -31,6 +32,9 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent
}
WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
+ // this initialzation of localSafeContext is reading a thread-local variable and that is depends on
+ // the ctor being executed on the same thread as the script, assuming it's being create by a script
+ _localSafeContext = hifi::scripting::isLocalAccessSafeThread();
_type = EntityTypes::Web;
}
@@ -241,6 +245,12 @@ glm::u8vec3 WebEntityItem::getColor() const {
});
}
+bool WebEntityItem::getLocalSafeContext() const {
+ return resultWithReadLock([&] {
+ return _localSafeContext;
+ });
+}
+
void WebEntityItem::setAlpha(float alpha) {
withWriteLock([&] {
_needsRenderUpdate |= _alpha != alpha;
diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h
index bb1e527712..b61e2b124f 100644
--- a/libraries/entities/src/WebEntityItem.h
+++ b/libraries/entities/src/WebEntityItem.h
@@ -74,6 +74,8 @@ public:
void setScriptURL(const QString& value);
QString getScriptURL() const;
+ bool getLocalSafeContext() const;
+
static const uint8_t DEFAULT_MAX_FPS;
void setMaxFPS(uint8_t value);
uint8_t getMaxFPS() const;
@@ -98,6 +100,7 @@ protected:
uint8_t _maxFPS;
WebInputMode _inputMode;
bool _showKeyboardFocusHighlight;
+ bool _localSafeContext { false };
};
#endif // hifi_WebEntityItem_h
diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp
index 68c8266e9f..de82dd6ace 100644
--- a/libraries/physics/src/EntityMotionState.cpp
+++ b/libraries/physics/src/EntityMotionState.cpp
@@ -84,48 +84,51 @@ EntityMotionState::~EntityMotionState() {
}
void EntityMotionState::updateServerPhysicsVariables() {
- if (_ownershipState != EntityMotionState::OwnershipState::LocallyOwned) {
- // only slam these values if we are NOT the simulation owner
- Transform localTransform;
- _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
- _serverPosition = localTransform.getTranslation();
- _serverRotation = localTransform.getRotation();
- _serverAcceleration = _entity->getAcceleration();
- _serverActionData = _entity->getDynamicData();
- _lastStep = ObjectMotionState::getWorldSimulationStep();
- }
+ // only slam these values if we are NOT the simulation owner
+ Transform localTransform;
+ _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
+ _serverPosition = localTransform.getTranslation();
+ _serverRotation = localTransform.getRotation();
+ _serverAcceleration = _entity->getAcceleration();
+ _serverActionData = _entity->getDynamicData();
+ _lastStep = ObjectMotionState::getWorldSimulationStep();
}
void EntityMotionState::handleDeactivation() {
- if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
- // Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities
- // at the last minute before deactivation --> the values stored in _server* and _body are stale.
- // We assume the EntityMotionState is the last to know, so we copy from EntityItem and let things sort themselves out.
- Transform localTransform;
- _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
- _serverPosition = localTransform.getTranslation();
- _serverRotation = localTransform.getRotation();
- _serverAcceleration = _entity->getAcceleration();
- _serverActionData = _entity->getDynamicData();
- _lastStep = ObjectMotionState::getWorldSimulationStep();
- } else {
- // copy _server data to entity
- Transform localTransform = _entity->getLocalTransform();
- localTransform.setTranslation(_serverPosition);
- localTransform.setRotation(_serverRotation);
- _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
- // and also to RigidBody
- btTransform worldTrans;
- worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
- worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
- _body->setWorldTransform(worldTrans);
- // no need to update velocities... should already be zero
- }
+ if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
+ // Some non-physical event (script-call or network-packet) has modified the entity's transform and/or
+ // velocities at the last minute before deactivation --> the values stored in _server* and _body are stale.
+ // We assume the EntityMotionState is the last to know, so we copy from EntityItem to _server* variables
+ // here but don't clear the flags --> the will body be set straight before next simulation step.
+ updateServerPhysicsVariables();
+ } else if (_body->isStaticOrKinematicObject() && _ownershipState != EntityMotionState::OwnershipState::LocallyOwned) {
+ // To allow the ESS to move entities around in a kinematic way we had to remove the requirement that
+ // every moving+simulated entity has an authoritative simulation owner. As a result, we cannot rely
+ // on a final authoritative update of kinmatic objects prior to deactivation in the local simulation.
+ // For this case (unowned kinematic objects) we update the _server* variables for good measure but
+ // leave the entity and body alone. They should have been updated correctly in the last call to
+ // EntityMotionState::getWorldTransform().
+ updateServerPhysicsVariables();
+ } else {
+ // copy _server data to entity
+ Transform localTransform = _entity->getLocalTransform();
+ localTransform.setTranslation(_serverPosition);
+ localTransform.setRotation(_serverRotation);
+ _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
+ // and also to RigidBody
+ btTransform worldTrans;
+ worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition()));
+ worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation()));
+ _body->setWorldTransform(worldTrans);
+ // no need to update velocities... should already be zero
+ }
}
// virtual
void EntityMotionState::handleEasyChanges(uint32_t& flags) {
- updateServerPhysicsVariables();
+ if (_ownershipState != EntityMotionState::OwnershipState::LocallyOwned) {
+ updateServerPhysicsVariables();
+ }
ObjectMotionState::handleEasyChanges(flags);
if (flags & Simulation::DIRTY_SIMULATOR_ID) {
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp
index c54f63690d..3465138e00 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.cpp
+++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp
@@ -795,11 +795,25 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url) {
}
void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) {
+ bool localSafeContext = hifi::scripting::isLocalAccessSafeThread();
if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "loadWebScreenOnTop", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl));
+ QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl), Q_ARG(bool, localSafeContext));
return;
}
+ loadHTMLSourceImpl(url, injectJavaScriptUrl, localSafeContext);
+}
+
+
+
+void TabletProxy::loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext) {
+ if (QThread::currentThread() != thread()) {
+ qCWarning(uiLogging) << __FUNCTION__ << "may not be called directly by scripts";
+ return;
+
+ }
+
+
QObject* root = nullptr;
if (!_toolbarMode && _qmlTabletRoot) {
root = _qmlTabletRoot;
@@ -808,22 +822,33 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJ
}
if (root) {
+ if (localSafeContext) {
+ hifi::scripting::setLocalAccessSafeThread(true);
+ }
QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL)));
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
if (_toolbarMode && _desktopWindow) {
QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false)));
}
QMetaObject::invokeMethod(root, "loadWebOnTop", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectJavaScriptUrl)));
+ hifi::scripting::setLocalAccessSafeThread(false);
}
_state = State::Web;
}
void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase) {
+ bool localSafeContext = hifi::scripting::isLocalAccessSafeThread();
if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "gotoWebScreen", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase));
+ QMetaObject::invokeMethod(this, "loadHTMLSourceImpl", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl), Q_ARG(bool, loadOtherBase), Q_ARG(bool, localSafeContext));
return;
}
+
+ loadHTMLSourceImpl(url, injectedJavaScriptUrl, loadOtherBase, localSafeContext);
+}
+
+void TabletProxy::loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext) {
+
QObject* root = nullptr;
if (!_toolbarMode && _qmlTabletRoot) {
root = _qmlTabletRoot;
@@ -832,6 +857,9 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
}
if (root) {
+ if (localSafeContext) {
+ hifi::scripting::setLocalAccessSafeThread(true);
+ }
if (loadOtherBase) {
QMetaObject::invokeMethod(root, "loadTabletWebBase", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl)));
} else {
@@ -841,6 +869,8 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
if (_toolbarMode && _desktopWindow) {
QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(false)));
}
+
+ hifi::scripting::setLocalAccessSafeThread(false);
_state = State::Web;
_currentPathLoaded = QVariant(url);
} else {
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index ba02ba25b0..9a5ff9efac 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -298,6 +298,10 @@ public:
*/
Q_INVOKABLE void loadQMLSourceImpl(const QVariant& path, bool resizable, bool localSafeContext);
+ Q_INVOKABLE void loadHTMLSourceImpl(const QVariant& url, const QString& injectJavaScriptUrl, bool localSafeContext);
+
+ Q_INVOKABLE void loadHTMLSourceImpl(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase, bool localSafeContext);
+
// FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success);
// it should be initialized internally so it cannot fail