From 6ef77bc46aeab1fb17186855271fd16284da9102 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 17 Dec 2015 15:46:44 -0800 Subject: [PATCH] Support the marketplace in QML --- examples/edit.js | 47 ++++++++++++------- interface/resources/qml/QmlWebWindow.qml | 16 ++++--- interface/src/Application.cpp | 2 +- interface/src/Application.h | 7 +-- libraries/networking/src/AbstractUriHandler.h | 19 ++++++++ libraries/octree/src/Octree.cpp | 2 +- libraries/ui/src/QmlWebWindowClass.cpp | 43 +++++++++++++++-- 7 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 libraries/networking/src/AbstractUriHandler.h diff --git a/examples/edit.js b/examples/edit.js index 59b6ae3e7d..074b43c8c1 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -140,9 +140,33 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", { }); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); +var marketplaceWindow = new OverlayWebWindow('Marketplace', "about:blank", 900, 700, false); marketplaceWindow.setVisible(false); +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting marketplace URL to " + url); + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + marketplaceWindow.raise(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + var toolBar = (function() { var that = {}, toolBar, @@ -413,12 +437,9 @@ var toolBar = (function() { newModelButtonDown = true; return true; } + if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { - if (marketplaceWindow.url != MARKETPLACE_URL) { - marketplaceWindow.setURL(MARKETPLACE_URL); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + toggleMarketplace(); return true; } @@ -1336,6 +1357,7 @@ function getPositionToCreateEntity() { } function importSVO(importURL) { + print("Import URL requested: " + importURL) if (!Entities.canAdjustLocks()) { Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -1574,11 +1596,7 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } else if (data.type == "showMarketplace") { - if (marketplaceWindow.url != data.url) { - marketplaceWindow.setURL(data.url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(); } else if (data.type == "action") { if (data.action == "moveSelectionToGrid") { if (selectionManager.hasSelection()) { @@ -1859,12 +1877,7 @@ var propertyMenu = PopupMenu(); propertyMenu.onSelectMenuItem = function(name) { if (propertyMenu.marketplaceID) { - var url = MARKETPLACE_URL + "/items/" + propertyMenu.marketplaceID; - if (marketplaceWindow.url != url) { - marketplaceWindow.setURL(url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(propertyMenu.marketplaceID); } }; diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 3eb01aa9ba..029d50519a 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -13,12 +13,18 @@ VrDialog { HifiConstants { id: hifi } title: "WebWindow" resizable: true + // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer + destroyOnCloseButton: false contentImplicitWidth: clientArea.implicitWidth contentImplicitHeight: clientArea.implicitHeight backgroundColor: "#7f000000" property url source: "about:blank" signal navigating(string url) + function stop() { + webview.stop(); + } + Component.onCompleted: { enabled = true @@ -26,18 +32,14 @@ VrDialog { webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); }); - webview.loadingChanged.connect(handleWebviewLoading) } function handleWebviewLoading(loadRequest) { - var HIFI_URL_PATTERN = /^hifi:\/\//; if (WebEngineView.LoadStartedStatus == loadRequest.status) { var newUrl = loadRequest.url.toString(); - if (newUrl.match(HIFI_URL_PATTERN)) { - root.navigating(newUrl); - } + root.navigating(newUrl) } } @@ -55,8 +57,10 @@ VrDialog { url: root.source anchors.fill: parent profile: WebEngineProfile { + id: webviewProfile httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)" - } + storageName: "qmlWebEngine" + } } } // item } // dialog diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e22b3913d0..7f27d97416 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4215,7 +4215,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); } -bool Application::canAcceptURL(const QString& urlString) { +bool Application::canAcceptURL(const QString& urlString) const { QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; diff --git a/interface/src/Application.h b/interface/src/Application.h index a665e925a9..0953aedd8c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "avatar/AvatarUpdate.h" #include "avatar/MyAvatar.h" @@ -88,7 +89,7 @@ class Application; #endif #define qApp (static_cast(QCoreApplication::instance())) -class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { +class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, public AbstractUriHandler { Q_OBJECT // TODO? Get rid of those @@ -219,8 +220,8 @@ public: QString getScriptsLocation(); void setScriptsLocation(const QString& scriptsLocation); - bool canAcceptURL(const QString& url); - bool acceptURL(const QString& url, bool defaultUpload = false); + virtual bool canAcceptURL(const QString& url) const override; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond(); diff --git a/libraries/networking/src/AbstractUriHandler.h b/libraries/networking/src/AbstractUriHandler.h new file mode 100644 index 0000000000..3d6ed472ec --- /dev/null +++ b/libraries/networking/src/AbstractUriHandler.h @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis on 2015/12/17 +// Copyright 2013-2015 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 +// + +#pragma once +#ifndef hifi_network_AbstractUriHandler_h +#define hifi_network_AbstractUriHandler_h + +class AbstractUriHandler { +public: + virtual bool canAcceptURL(const QString& url) const = 0; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) = 0; +}; + +#endif diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 636d1a9a1a..652089ebb1 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1867,7 +1867,7 @@ bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputSt QByteArray jsonBuffer; char* rawData = new char[READ_JSON_BUFFER_SIZE]; - while (true) { + while (!inputStream.atEnd()) { int got = inputStream.readRawData(rawData, READ_JSON_BUFFER_SIZE - 1); if (got < 0) { qCritical() << "error while reading from json stream"; diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index d5cdc1fde9..68a45a5bff 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -10,7 +10,10 @@ #include +#include #include +#include +#include #include #include @@ -22,6 +25,7 @@ #include #include +#include #include #include @@ -88,7 +92,7 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi QmlWebWindowClass* retVal { nullptr }; const QString title = context->argument(0).toString(); QString url = context->argument(1).toString(); - if (!url.startsWith("http") && !url.startsWith("file://")) { + if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) { url = QUrl::fromLocalFile(url).toString(); } const int width = std::max(100, std::min(1280, context->argument(2).toInt32()));; @@ -119,7 +123,23 @@ QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) } void QmlWebWindowClass::handleNavigation(const QString& url) { - DependencyManager::get()->handleLookupString(url); + bool handled = false; + + if (url.contains(HIFI_URL_PATTERN)) { + DependencyManager::get()->handleLookupString(url); + handled = true; + } else { + static auto handler = dynamic_cast(qApp); + if (handler) { + if (handler->canAcceptURL(url)) { + handled = handler->acceptURL(url); + } + } + } + + if (handled) { + QMetaObject::invokeMethod(_qmlWindow, "stop", Qt::AutoConnection); + } } void QmlWebWindowClass::setVisible(bool visible) { @@ -202,6 +222,7 @@ QString QmlWebWindowClass::getURL() const { QMetaObject::invokeMethod(const_cast(this), "getURL", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, result)); return result; } + return _qmlWindow->property(URL_PROPERTY).toString(); } @@ -209,7 +230,23 @@ void QmlWebWindowClass::setURL(const QString& urlString) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setURL", Qt::QueuedConnection, Q_ARG(QString, urlString)); } - _qmlWindow->setProperty(URL_PROPERTY, urlString); + + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + + QUrl url(urlString); + qDebug() << "Url: " << urlString; + qDebug() << "Host: " << url.host(); + if (url.host() == ALLOWED_HOST) { + QUrlQuery query(url); + if (query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + AccountManager& accountManager = AccountManager::getInstance(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + qDebug() << "New URL " << url; + } + } + _qmlWindow->setProperty(URL_PROPERTY, url.toString()); }