Merge branch 'master' of github.com:highfidelity/hifi into feetposition

This commit is contained in:
RebeccaStankus 2019-08-30 14:20:58 -07:00
commit 37304f57ea
18 changed files with 215 additions and 49 deletions

View file

@ -0,0 +1,11 @@
import QtQuick 2.0
import "../../controls" as Controls
Controls.WebView {
id: root
function fromScript(message) {
root.url = message.url;
}
}

View file

@ -139,6 +139,7 @@
#include <SoundCacheScriptingInterface.h>
#include <ui/TabletScriptingInterface.h>
#include <ui/ToolbarScriptingInterface.h>
#include <ui/types/ContextAwareProfile.h>
#include <Tooltip.h>
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
@ -3285,6 +3286,9 @@ void Application::initializeUi() {
}
return result.toPoint();
});
// BUGZ-1365 - the root context should explicitly default to being unable to load local HTML content
ContextAwareProfile::restrictContext(offscreenUi->getSurfaceContext(), true);
offscreenUi->resume();
#endif
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
@ -7457,7 +7461,7 @@ void Application::addingEntityWithCertificate(const QString& certificateID, cons
ledger->updateLocation(certificateID, placeName);
}
void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) {
void Application::registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) {
scriptEngine->setEmitScriptUpdatesFunction([this]() {
SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer);
@ -7496,9 +7500,19 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
qScriptRegisterMetaType(scriptEngine.data(), RayToOverlayIntersectionResultToScriptValue,
RayToOverlayIntersectionResultFromScriptValue);
bool clientScript = scriptEngine->isClientScript();
#if !defined(DISABLE_QML)
scriptEngine->registerGlobalObject("OffscreenFlags", getOffscreenUI()->getFlags());
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
if (clientScript) {
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
} else {
auto desktopScriptingInterface = new DesktopScriptingInterface(nullptr, true);
scriptEngine->registerGlobalObject("Desktop", desktopScriptingInterface);
if (QThread::currentThread() != thread()) {
desktopScriptingInterface->moveToThread(thread());
}
}
#endif
qScriptRegisterMetaType(scriptEngine.data(), wrapperToScriptValue<ToolbarProxy>, wrapperFromScriptValue<ToolbarProxy>);
@ -7523,7 +7537,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
LocationScriptingInterface::locationSetter);
bool clientScript = scriptEngine->isClientScript();
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML)
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);

View file

@ -252,7 +252,7 @@ public:
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
virtual void registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) override;
virtual void registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) override;
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); }
virtual QThread* getMainThread() override { return thread(); }

View file

@ -52,6 +52,9 @@ static const QVariantMap DOCK_AREA {
{ "RIGHT", DockArea::RIGHT }
};
DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted)
: QObject(parent), _restricted(restricted) { }
int DesktopScriptingInterface::getWidth() {
QSize size = qApp->getWindow()->windowHandle()->screen()->virtualSize();
return size.width();
@ -128,7 +131,7 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindow(const QString&
return nullptr;
}
return new InteractiveWindow(sourceUrl, properties);
return new InteractiveWindow(sourceUrl, properties, _restricted);
}
InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread) {
@ -139,7 +142,7 @@ InteractiveWindowPointer DesktopScriptingInterface::createWindowOnThread(const Q
if (!urlValidator(sourceUrl)) {
return nullptr;
}
InteractiveWindowPointer window = new InteractiveWindow(sourceUrl, properties);
InteractiveWindowPointer window = new InteractiveWindow(sourceUrl, properties, _restricted);
window->moveToThread(targetThread);
return window;
}

View file

@ -54,6 +54,8 @@ class DesktopScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
public:
DesktopScriptingInterface(QObject* parent= nullptr, bool restricted = false);
/**jsdoc
* Sets the opacity of the HUD surface.
* @function Desktop.setHUDAlpha
@ -106,6 +108,7 @@ private:
static QVariantMap getDockArea();
Q_INVOKABLE static QVariantMap getPresentationMode();
const bool _restricted;
};

View file

@ -18,6 +18,9 @@
#include <QtQuick/QQuickWindow>
#include <QQuickView>
#include <ui/types/ContextAwareProfile.h>
#include <ui/types/HFWebEngineProfile.h>
#include <ui/types/FileTypeProfile.h>
#include <DependencyManager.h>
#include <DockWidget.h>
#include <RegisteredMetaTypes.h>
@ -134,7 +137,7 @@ void InteractiveWindow::emitMainWindowResizeEvent() {
* Set at window creation. Possible flag values are provided as {@link Desktop|Desktop.ALWAYS_ON_TOP} and {@link Desktop|Desktop.CLOSE_BUTTON_HIDES}.
* Additional flag values can be found on Qt's website at https://doc.qt.io/qt-5/qt.html#WindowType-enum.
*/
InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties) {
InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties, bool restricted) {
InteractiveWindowPresentationMode presentationMode = InteractiveWindowPresentationMode::Native;
if (properties.contains(PRESENTATION_MODE_PROPERTY)) {
@ -228,9 +231,16 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
_dockWidget->setObjectName("DockedWidget");
mainWindow->addDockWidget(dockArea, _dockWidget.get());
} else {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(CONTENT_WINDOW_QML, [&](QQmlContext* context, QObject* object) {
auto contextInitLambda = [&](QQmlContext* context) {
// If the restricted flag is on, the web content will not be able to access local files
ContextAwareProfile::restrictContext(context, restricted);
#if !defined(Q_OS_ANDROID)
FileTypeProfile::registerWithContext(context);
HFWebEngineProfile::registerWithContext(context);
#endif
};
auto objectInitLambda = [&](QQmlContext* context, QObject* object) {
_qmlWindowProxy = std::shared_ptr<QmlWindowProxy>(new QmlWindowProxy(object, nullptr), qmlWindowProxyDeleter);
context->setContextProperty(EVENT_BRIDGE_PROPERTY, _interactiveWindowProxy.get());
if (properties.contains(ADDITIONAL_FLAGS_PROPERTY)) {
@ -286,7 +296,11 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
}
object->setObjectName("InteractiveWindow");
object->setProperty(SOURCE_PROPERTY, sourceURL);
});
};
auto offscreenUi = DependencyManager::get<OffscreenUi>();
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(CONTENT_WINDOW_QML, objectInitLambda, contextInitLambda);
}
}

View file

@ -126,7 +126,7 @@ class InteractiveWindow : public QObject {
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
public:
InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties);
InteractiveWindow(const QString& sourceUrl, const QVariantMap& properties, bool restricted);
~InteractiveWindow();
private:

View file

@ -18,7 +18,7 @@
class AbstractScriptingServicesInterface {
public:
/// Registers application specific services with a script engine.
virtual void registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) = 0;
virtual void registerScriptEngineWithApplicationServices(const ScriptEnginePointer& scriptEngine) = 0;
};

View file

@ -136,13 +136,9 @@ void QmlWindowClass::initQml(QVariantMap properties) {
#if !defined(Q_OS_ANDROID)
// If the restricted flag is on, override the FileTypeProfile and HFWebEngineProfile objects in the
// QML surface root context with local ones
qDebug() << "Context initialization lambda";
if (_restricted) {
qDebug() << "Restricting web content";
ContextAwareProfile::restrictContext(context);
FileTypeProfile::registerWithContext(context);
HFWebEngineProfile::registerWithContext(context);
}
ContextAwareProfile::restrictContext(context, _restricted);
FileTypeProfile::registerWithContext(context);
HFWebEngineProfile::registerWithContext(context);
#endif
};

View file

@ -11,22 +11,47 @@
#include "ContextAwareProfile.h"
#if !defined(Q_OS_ANDROID)
#include <cassert>
#include <QtCore/QThread>
#include <QtQml/QQmlContext>
#include <shared/QtHelpers.h>
#include <SharedUtil.h>
static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess";
ContextAwareProfile::ContextAwareProfile(QQmlContext* parent) :
QQuickWebEngineProfile(parent), _context(parent) { }
void ContextAwareProfile::restrictContext(QQmlContext* context) {
context->setContextProperty(RESTRICTED_FLAG_PROPERTY, true);
ContextAwareProfile::ContextAwareProfile(QQmlContext* context) :
ContextAwareProfileParent(context), _context(context) {
assert(context);
}
bool ContextAwareProfile::isRestricted(QQmlContext* context) {
return context->contextProperty(RESTRICTED_FLAG_PROPERTY).toBool();
void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) {
context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict);
}
#endif
bool ContextAwareProfile::isRestrictedInternal() {
if (QThread::currentThread() != thread()) {
bool restrictedResult = false;
BLOCKING_INVOKE_METHOD(this, "isRestrictedInternal", Q_RETURN_ARG(bool, restrictedResult));
return restrictedResult;
}
QVariant variant = _context->contextProperty(RESTRICTED_FLAG_PROPERTY);
if (variant.isValid()) {
return variant.toBool();
}
// BUGZ-1365 - we MUST defalut to restricted mode in the absence of a flag, or it's too easy for someone to make
// a new mechanism for loading web content that fails to restrict access to local files
return true;
}
bool ContextAwareProfile::isRestricted() {
auto now = usecTimestampNow();
if (now > _cacheExpiry) {
_cachedValue = isRestrictedInternal();
_cacheExpiry = now + MAX_CACHE_AGE;
}
return _cachedValue;
}

View file

@ -17,26 +17,40 @@
#include <QtWebEngine/QQuickWebEngineProfile>
#include <QtWebEngineCore/QWebEngineUrlRequestInterceptor>
using ContextAwareProfileParent = QQuickWebEngineProfile;
using RequestInterceptorParent = QWebEngineUrlRequestInterceptor;
#else
#include <QtCore/QObject>
using ContextAwareProfileParent = QObject;
using RequestInterceptorParent = QObject;
#endif
#include <NumericalConstants.h>
class QQmlContext;
class ContextAwareProfile : public QQuickWebEngineProfile {
class ContextAwareProfile : public ContextAwareProfileParent {
Q_OBJECT
public:
static void restrictContext(QQmlContext* context);
static bool isRestricted(QQmlContext* context);
QQmlContext* getContext() const { return _context; }
static void restrictContext(QQmlContext* context, bool restrict = true);
bool isRestricted();
Q_INVOKABLE bool isRestrictedInternal();
protected:
class RequestInterceptor : public QWebEngineUrlRequestInterceptor {
class RequestInterceptor : public RequestInterceptorParent {
public:
RequestInterceptor(ContextAwareProfile* parent) : QWebEngineUrlRequestInterceptor(parent), _profile(parent) {}
QQmlContext* getContext() const { return _profile->getContext(); }
RequestInterceptor(ContextAwareProfile* parent) : RequestInterceptorParent(parent), _profile(parent) { }
bool isRestricted() { return _profile->isRestricted(); }
protected:
ContextAwareProfile* _profile;
};
ContextAwareProfile(QQmlContext* parent);
QQmlContext* _context;
QQmlContext* _context{ nullptr };
bool _cachedValue{ false };
quint64 _cacheExpiry{ 0 };
constexpr static quint64 MAX_CACHE_AGE = MSECS_PER_SECOND;
};
#endif
#endif // hifi_FileTypeProfile_h

View file

@ -29,8 +29,8 @@ FileTypeProfile::FileTypeProfile(QQmlContext* parent) :
}
void FileTypeProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info, getContext());
RequestFilters::interceptFileType(info, getContext());
RequestFilters::interceptHFWebEngineRequest(info, isRestricted());
RequestFilters::interceptFileType(info);
}
void FileTypeProfile::registerWithContext(QQmlContext* context) {

View file

@ -28,7 +28,7 @@ HFWebEngineProfile::HFWebEngineProfile(QQmlContext* parent) : Parent(parent)
}
void HFWebEngineProfile::RequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info, getContext());
RequestFilters::interceptHFWebEngineRequest(info, isRestricted());
}
void HFWebEngineProfile::registerWithContext(QQmlContext* context) {

View file

@ -63,8 +63,8 @@ namespace {
}
}
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
if (ContextAwareProfile::isRestricted(context) && blockLocalFiles(info)) {
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, bool restricted) {
if (restricted && blockLocalFiles(info)) {
return;
}
@ -90,7 +90,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info,
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
}
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context) {
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
QString filename = info.requestUrl().fileName();
if (isScript(filename) || isJSON(filename)) {
static const QString CONTENT_HEADER = "Accept";

View file

@ -24,8 +24,8 @@ class QQmlContext;
class RequestFilters : public QObject {
public:
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, QQmlContext* context);
static void interceptFileType(QWebEngineUrlRequestInfo& info, QQmlContext* context);
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info, bool restricted);
static void interceptFileType(QWebEngineUrlRequestInfo& info);
};
#endif

View file

@ -0,0 +1,55 @@
"use strict";
// Created by Bradley Austin Davis on 2019/08/29
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
(function() { // BEGIN LOCAL_SCOPE
var QML_URL = "qrc:/qml/hifi/tablet/DynamicWebview.qml"
var LOCAL_FILE_URL = "file:///C:/test-file.html"
var TABLET_BUTTON_NAME = "SCRIPT";
var ICONS = {
icon: "icons/tablet-icons/menu-i.svg",
activeIcon: "icons/tablet-icons/meni-a.svg"
};
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var button = tablet.addButton({
icon: ICONS.icon,
activeIcon: ICONS.activeIcon,
text: TABLET_BUTTON_NAME,
sortOrder: 1
});
function onClicked() {
var window = Desktop.createWindow(QML_URL, {
title: "Local file access test",
additionalFlags: Desktop.ALWAYS_ON_TOP,
presentationMode: Desktop.PresentationMode.NATIVE,
size: { x: 640, y: 480 },
visible: true,
position: { x: 100, y: 100 },
});
window.sendToQml({ url: LOCAL_FILE_URL });
}
button.clicked.connect(onClicked);
Script.scriptEnding.connect(function () {
button.clicked.disconnect(onClicked);
tablet.removeButton(button);
});
}()); // END LOCAL_SCOPE

View file

@ -0,0 +1,30 @@
"use strict";
// Created by Bradley Austin Davis on 2019/08/29
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
var QML_URL = "qrc:/qml/hifi/tablet/DynamicWebview.qml"
var LOCAL_FILE_URL = "file:///C:/test-file.html"
var _this = this;
_this.preload = function (pEntityID) {
var window = Desktop.createWindow(QML_URL, {
title: "Local file access test",
additionalFlags: Desktop.ALWAYS_ON_TOP,
presentationMode: Desktop.PresentationMode.NATIVE,
size: { x: 640, y: 480 },
visible: true,
position: { x: 100, y: 100 },
});
window.sendToQml({ url: LOCAL_FILE_URL });
};
_this.unload = function () {
console.log("QQQ on preload");
};
});

View file

@ -64,7 +64,9 @@ function simplifiedStatusIndicator(properties) {
queryParamString += "&displayName=" + displayNameToSend;
queryParamString += "&status=" + currentStatus;
queryParamString += "&organization=" + location.hostname;
var domainID = location.domainID;
domainID = domainID.substring(1, domainID.length - 1);
queryParamString += "&organization=" + domainID;
var uri = REQUEST_URL + "?" + queryParamString;