History

isolate calls to the QtScript libraries to an interface we control

Heather Anderson committed 5 years ago

Commit 1e018db1 parent 713b29e260 files · +6904 −5064Browse files

Showing the changes to libraries/script-engine/src/AssetScriptingInterface.cpp (1 of 260 files). Show all

Unified · Split

libraries/script-engine/src/AssetScriptingInterface.cpp+84−75
@@ -13,18 +13,20 @@ · 12 unmodified lines
1313
1414 #include <QMimeDatabase>
1515 #include <QThread>
16−#include <QtScript/QScriptEngine>
1716
1817 #include <AssetRequest.h>
1918 #include <AssetUpload.h>
2019 #include <AssetUtils.h>
21−#include <BaseScriptEngine.h>
2220 #include <MappingRequest.h>
2321 #include <NodeList.h>
2422
2523 #include <RegisteredMetaTypes.h>
2624 #include "ScriptEngine.h"
25+#include "ScriptEngineCast.h"
2726 #include "ScriptEngineLogging.h"
27+#include "ScriptManager.h"
28+#include "ScriptValue.h"
29+#include "ScriptValueUtils.h"
2830
2931 #include <shared/QtHelpers.h>
3032 #include <Gzip.h>
@@ -35,7 +37,9 @@ using Promise = MiniPromise::Promise; · 4 unmodified lines
3537
3638 AssetScriptingInterface::AssetScriptingInterface(QObject* parent) : BaseAssetScriptingInterface(parent) {
3739 qCDebug(scriptengine) << "AssetScriptingInterface::AssetScriptingInterface" << parent;
38− MiniPromise::registerMetaTypes(parent);
40+
41+ auto scriptManager = qobject_cast<ScriptManager*>(parent);
42+ scriptRegisterMetaType(scriptManager->engine().get(), promiseToScriptValue, promiseFromScriptValue);
3943 }
4044
4145 #define JS_VERIFY(cond, error) { if (!this->jsVerify(cond, error)) { return; } }
@@ -59,16 +63,17 @@ bool AssetScriptingInterface::initializeCache() { · 17 unmodified lines
5963 }
6064 }
6165
62−void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) {
66+void AssetScriptingInterface::uploadData(QString data, ScriptValuePointer callback) {
6367 auto handler = jsBindCallback(thisObject(), callback);
6468 QByteArray dataByteArray = data.toUtf8();
6569 auto upload = DependencyManager::get<AssetClient>()->createUpload(dataByteArray);
6670
6771 Promise deferred = makePromise(__FUNCTION__);
72+ auto scriptEngine = engine();
6873 deferred->ready([=](QString error, QVariantMap result) {
6974 auto url = result.value("url").toString();
7075 auto hash = result.value("hash").toString();
71− jsCallback(handler, url, hash);
76+ jsCallback(handler, scriptEngine->newValue(url), scriptEngine->newValue(hash));
7277 });
7378
7479 connect(upload, &AssetUpload::finished, upload, [deferred](AssetUpload* upload, const QString& hash) {
@@ -83,12 +88,13 @@ void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) { · 8 unmodified lines
8388 upload->start();
8489 }
8590
86−void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValue callback) {
91+void AssetScriptingInterface::setMapping(QString path, QString hash, ScriptValuePointer callback) {
8792 auto handler = jsBindCallback(thisObject(), callback);
8893 auto setMappingRequest = assetClient()->createSetMappingRequest(path, hash);
8994 Promise deferred = makePromise(__FUNCTION__);
95+ auto scriptEngine = engine();
9096 deferred->ready([=](QString error, QVariantMap result) {
91− jsCallback(handler, error, result);
97+ jsCallback(handler, scriptEngine->newValue(error), result);
9298 });
9399
94100 connect(setMappingRequest, &SetMappingRequest::finished, setMappingRequest, [deferred](SetMappingRequest* request) {
@@ -107,7 +113,7 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu · 12 unmodified lines
107113 * @typedef {object} Assets.DownloadDataError
108114 * @property {string} errorMessage - <code>""</code> if the download was successful, otherwise a description of the error.
109115 */
110−void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
116+void AssetScriptingInterface::downloadData(QString urlString, ScriptValuePointer callback) {
111117 // FIXME: historically this API method failed silently when given a non-atp prefixed
112118 // urlString (or if the AssetRequest failed).
113119 // .. is that by design or could we update without breaking things to provide better feedback to scripts?
@@ -123,9 +129,10 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb · 9 unmodified lines
123129 auto assetRequest = assetClient->createRequest(hash);
124130
125131 Promise deferred = makePromise(__FUNCTION__);
132+ auto scriptEngine = engine();
126133 deferred->ready([=](QString error, QVariantMap result) {
127134 // FIXME: to remain backwards-compatible the signature here is "callback(data, n/a)"
128− jsCallback(handler, result.value("data").toString(), { { "errorMessage", error } });
135+ jsCallback(handler, scriptEngine->newValue(result.value("data").toString()), { { "errorMessage", error } });
129136 });
130137
131138 connect(assetRequest, &AssetRequest::finished, assetRequest, [deferred](AssetRequest* request) {
@@ -149,7 +156,7 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb · 17 unmodified lines
149156 assetRequest->start();
150157 }
151158
152−void AssetScriptingInterface::setBakingEnabled(QString path, bool enabled, QScriptValue callback) {
159+void AssetScriptingInterface::setBakingEnabled(QString path, bool enabled, ScriptValuePointer callback) {
153160 auto setBakingEnabledRequest = DependencyManager::get<AssetClient>()->createSetBakingEnabledRequest({ path }, enabled);
154161
155162 Promise deferred = jsPromiseReady(makePromise(__FUNCTION__), thisObject(), callback);
@@ -179,14 +186,15 @@ void AssetScriptingInterface::sendFakedHandshake() { · 23 unmodified lines
179186
180187 #endif
181188
182−void AssetScriptingInterface::getMapping(QString asset, QScriptValue callback) {
189+void AssetScriptingInterface::getMapping(QString asset, ScriptValuePointer callback) {
183190 auto path = AssetUtils::getATPUrl(asset).path();
184191 auto handler = jsBindCallback(thisObject(), callback);
185192 JS_VERIFY(AssetUtils::isValidFilePath(path), "invalid ATP file path: " + asset + "(path:"+path+")");
186− JS_VERIFY(callback.isFunction(), "expected second parameter to be a callback function");
193+ JS_VERIFY(callback->isFunction(), "expected second parameter to be a callback function");
187194 Promise promise = getAssetInfo(path);
195+ auto scriptEngine = engine();
188196 promise->ready([=](QString error, QVariantMap result) {
189− jsCallback(handler, error, result.value("hash").toString());
197+ jsCallback(handler, scriptEngine->newValue(error), scriptEngine->newValue(result.value("hash").toString()));
190198 });
191199 }
192200
@@ -202,45 +210,46 @@ bool AssetScriptingInterface::jsVerify(bool condition, const QString& error) { · 9 unmodified lines
202210 return false;
203211 }
204212
205−QScriptValue AssetScriptingInterface::jsBindCallback(QScriptValue scope, QScriptValue callback) {
206− QScriptValue handler = ::makeScopedHandlerObject(scope, callback);
207− QScriptValue value = handler.property("callback");
208− if (!jsVerify(handler.isObject() && value.isFunction(),
209− QString("jsBindCallback -- .callback is not a function (%1)").arg(value.toVariant().typeName()))) {
210− return QScriptValue();
213+ScriptValuePointer AssetScriptingInterface::jsBindCallback(ScriptValuePointer scope, ScriptValuePointer callback) {
214+ ScriptValuePointer handler = ::makeScopedHandlerObject(scope, callback);
215+ ScriptValuePointer value = handler->property("callback");
216+ if (!jsVerify(handler->isObject() && value->isFunction(),
217+ QString("jsBindCallback -- .callback is not a function (%1)").arg(value->toVariant().typeName()))) {
218+ return ScriptValuePointer();
211219 }
212220 return handler;
213221 }
214222
215−Promise AssetScriptingInterface::jsPromiseReady(Promise promise, QScriptValue scope, QScriptValue callback) {
223+Promise AssetScriptingInterface::jsPromiseReady(Promise promise, ScriptValuePointer scope, ScriptValuePointer callback) {
216224 auto handler = jsBindCallback(scope, callback);
217− if (!jsVerify(handler.isValid(), "jsPromiseReady -- invalid callback handler")) {
225+ if (!jsVerify(handler->isValid(), "jsPromiseReady -- invalid callback handler")) {
218226 return nullptr;
219227 }
220− return promise->ready([this, handler](QString error, QVariantMap result) {
221− jsCallback(handler, error, result);
228+ auto scriptEngine = engine();
229+ return promise->ready([this, handler, scriptEngine](QString error, QVariantMap result) {
230+ jsCallback(handler, scriptEngine->newValue(error), result);
222231 });
223232 }
224233
225−void AssetScriptingInterface::jsCallback(const QScriptValue& handler,
226− const QScriptValue& error, const QScriptValue& result) {
234+void AssetScriptingInterface::jsCallback(const ScriptValuePointer& handler,
235+ const ScriptValuePointer& error, const ScriptValuePointer& result) {
227236 Q_ASSERT(thread() == QThread::currentThread());
228− auto errorValue = !error.toBool() ? QScriptValue::NullValue : error;
229− JS_VERIFY(handler.isObject() && handler.property("callback").isFunction(),
237+ auto errorValue = !error->toBool() ? engine()->nullValue() : error;
238+ JS_VERIFY(handler->isObject() && handler->property("callback")->isFunction(),
230239 QString("jsCallback -- .callback is not a function (%1)")
231− .arg(handler.property("callback").toVariant().typeName()));
240+ .arg(handler->property("callback")->toVariant().typeName()));
232241 ::callScopedHandlerObject(handler, errorValue, result);
233242 }
234243
235−void AssetScriptingInterface::jsCallback(const QScriptValue& handler,
236− const QScriptValue& error, const QVariantMap& result) {
244+void AssetScriptingInterface::jsCallback(const ScriptValuePointer& handler,
245+ const ScriptValuePointer& error, const QVariantMap& result) {
237246 Q_ASSERT(thread() == QThread::currentThread());
238− Q_ASSERT(handler.engine());
239− auto engine = handler.engine();
247+ Q_ASSERT(handler->engine());
248+ auto engine = handler->engine();
240249 jsCallback(handler, error, engine->toScriptValue(result));
241250 }
242251
243−void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
252+void AssetScriptingInterface::deleteAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
244253 jsVerify(false, "TODO: deleteAsset API");
245254 }
246255
@@ -270,14 +279,14 @@ void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue sco · 23 unmodified lines
270279 * @property {boolean} [wasRedirected] - <code>true</code> if the downloaded data is the baked version of the asset,
271280 * <code>false</code> if it isn't baked.
272281 */
273−void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
274− JS_VERIFY(options.isObject() || options.isString(), "expected request options Object or URL as first parameter");
275−
276− auto decompress = options.property("decompress").toBool() || options.property("compressed").toBool();
277− auto responseType = options.property("responseType").toString().toLower();
278− auto url = options.property("url").toString();
279− if (options.isString()) {
280− url = options.toString();
282+void AssetScriptingInterface::getAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
283+ JS_VERIFY(options->isObject() || options->isString(), "expected request options Object or URL as first parameter");
284+
285+ auto decompress = options->property("decompress")->toBool() || options->property("compressed")->toBool();
286+ auto responseType = options->property("responseType")->toString().toLower();
287+ auto url = options->property("url")->toString();
288+ if (options->isString()) {
289+ url = options->toString();
281290 }
282291 if (responseType.isEmpty()) {
283292 responseType = "text";
@@ -336,10 +345,10 @@ void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, · 52 unmodified lines
336345 * @property {boolean} [wasRedirected] - <code>true</code> if the resolved data is for the baked version of the asset,
337346 * <code>false</code> if it isn't.
338347 */
339−void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
348+void AssetScriptingInterface::resolveAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
340349 const QString& URL{ "url" };
341350
342− auto url = (options.isString() ? options : options.property(URL)).toString();
351+ auto url = (options->isString() ? options : options->property(URL))->toString();
343352 auto asset = AssetUtils::getATPUrl(url).path();
344353
345354 JS_VERIFY(AssetUtils::isValidFilePath(asset) || AssetUtils::isValidHash(asset),
@@ -363,10 +372,10 @@ void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue sc · 17 unmodified lines
363372 * @property {string|object|ArrayBuffer} [response] - The decompressed data.
364373 * @property {Assets.ResponseType} [responseType] - The type of the decompressed data in <code>response</code>.
365374 */
366−void AssetScriptingInterface::decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback) {
367− auto data = options.property("data");
368− QByteArray dataByteArray = qscriptvalue_cast<QByteArray>(data);
369− auto responseType = options.property("responseType").toString().toLower();
375+void AssetScriptingInterface::decompressData(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
376+ auto data = options->property("data");
377+ QByteArray dataByteArray = scriptvalue_cast<QByteArray>(data);
378+ auto responseType = options->property("responseType")->toString().toLower();
370379 if (responseType.isEmpty()) {
371380 responseType = "text";
372381 }
@@ -404,10 +413,10 @@ namespace { · 31 unmodified lines
404413 * @property {string} [contentType] - The MIME type of the compressed data, i.e., <code>"application/gzip"</code>.
405414 * @property {ArrayBuffer} [data] - The compressed data.
406415 */
407−void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue scope, QScriptValue callback) {
408− auto data = options.property("data").isValid() ? options.property("data") : options;
409− QByteArray dataByteArray = data.isString() ? data.toString().toUtf8() : qscriptvalue_cast<QByteArray>(data);
410− int level = options.property("level").isNumber() ? options.property("level").toInt32() : DEFAULT_GZIP_COMPRESSION_LEVEL;
416+void AssetScriptingInterface::compressData(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
417+ auto data = options->property("data")->isValid() ? options->property("data") : options;
418+ QByteArray dataByteArray = data->isString() ? data->toString().toUtf8() : scriptvalue_cast<QByteArray>(data);
419+ int level = options->property("level")->isNumber() ? options->property("level")->toInt32() : DEFAULT_GZIP_COMPRESSION_LEVEL;
411420 JS_VERIFY(level >= DEFAULT_GZIP_COMPRESSION_LEVEL || level <= MAX_GZIP_COMPRESSION_LEVEL, QString("invalid .level %1").arg(level));
412421 jsPromiseReady(compressBytes(dataByteArray, level), scope, callback);
413422 }
@@ -433,18 +442,18 @@ void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue sc · 19 unmodified lines
433442 * @property {string} [url] - The <code>atp:</code> URL of the content: using the path if specified, otherwise the hash.
434443 * @property {string} [path] - The uploaded content's mapped path, if specified.
435444 */
436−void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) {
437− auto compress = options.property("compress").toBool() || options.property("compressed").toBool();
438− auto data = options.isObject() ? options.property("data") : options;
439− auto rawPath = options.property("path").toString();
445+void AssetScriptingInterface::putAsset(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
446+ auto compress = options->property("compress")->toBool() || options->property("compressed")->toBool();
447+ auto data = options->isObject() ? options->property("data") : options;
448+ auto rawPath = options->property("path")->toString();
440449 auto path = AssetUtils::getATPUrl(rawPath).path();
441450
442− QByteArray dataByteArray = data.isString() ? data.toString().toUtf8() : qscriptvalue_cast<QByteArray>(data);
451+ QByteArray dataByteArray = data->isString() ? data->toString().toUtf8() : scriptvalue_cast<QByteArray>(data);
443452
444453 JS_VERIFY(path.isEmpty() || AssetUtils::isValidFilePath(path),
445454 QString("expected valid ATP file path '%1' ('%2')").arg(rawPath).arg(path));
446455 JS_VERIFY(dataByteArray.size() > 0,
447− QString("expected non-zero .data (got %1 / #%2 bytes)").arg(data.toVariant().typeName()).arg(dataByteArray.size()));
456+ QString("expected non-zero .data (got %1 / #%2 bytes)").arg(data->toVariant().typeName()).arg(dataByteArray.size()));
448457
449458 // [compressed] => uploaded to server => [mapped to path]
450459 Promise prepared = makePromise("putAsset::prepared");
@@ -489,8 +498,8 @@ void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, · 38 unmodified lines
489498 * @property {string} url - The URL of the cached asset to get information on. Must start with <code>"atp:"</code> or
490499 * <code>"cache:"</code>.
491500 */
492−void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback) {
493− QString url = options.isString() ? options.toString() : options.property("url").toString();
501+void AssetScriptingInterface::queryCacheMeta(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
502+ QString url = options->isString() ? options->toString() : options->property("url")->toString();
494503 JS_VERIFY(QUrl(url).isValid(), QString("Invalid URL '%1'").arg(url));
495504 jsPromiseReady(Parent::queryCacheMeta(url), scope, callback);
496505 }
@@ -504,16 +513,16 @@ void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue · 7 unmodified lines
504513 * @property {string} url - The URL of the asset to load from cache. Must start with <code>"atp:"</code> or
505514 * <code>"cache:"</code>.
506515 */
507−void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback) {
516+void AssetScriptingInterface::loadFromCache(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
508517 QString url, responseType;
509518 bool decompress = false;
510− if (options.isString()) {
511− url = options.toString();
519+ if (options->isString()) {
520+ url = options->toString();
512521 responseType = "text";
513522 } else {
514− url = options.property("url").toString();
515− responseType = options.property("responseType").isValid() ? options.property("responseType").toString() : "text";
516− decompress = options.property("decompress").toBool() || options.property("compressed").toBool();
523+ url = options->property("url")->toString();
524+ responseType = options->property("responseType")->isValid() ? options->property("responseType")->toString() : "text";
525+ decompress = options->property("decompress")->toBool() || options->property("compressed")->toBool();
517526 }
518527 JS_VERIFY(QUrl(url).isValid(), QString("Invalid URL '%1'").arg(url));
519528 JS_VERIFY(RESPONSE_TYPES.contains(responseType),
@@ -523,14 +532,14 @@ void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue s · 3 unmodified lines
523532 }
524533
525534 bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) {
526− auto scriptEngine = qobject_cast<ScriptEngine*>(engine());
527− if (!scriptEngine) {
535+ auto scriptManager = engine()->manager();
536+ if (!scriptManager) {
528537 return false;
529538 }
530539 // allow cache writes only from Client, EntityServer and Agent scripts
531540 bool isAllowedContext = (
532− scriptEngine->isClientScript() ||
533− scriptEngine->isAgentScript()
541+ scriptManager->isClientScript() ||
542+ scriptManager->isAgentScript()
534543 );
535544 if (!isAllowedContext) {
536545 return false;
@@ -546,17 +555,17 @@ bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) { · 9 unmodified lines
546555 * @property {string} [url] - The URL to associate with the cache item. Must start with <code>"atp:"</code> or
547556 * <code>"cache:"</code>. If not specified, the URL is <code>"atp:"</code> followed by the SHA256 hash of the content.
548557 */
549−void AssetScriptingInterface::saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback) {
550− JS_VERIFY(options.isObject(), QString("expected options object as first parameter not: %1").arg(options.toVariant().typeName()));
558+void AssetScriptingInterface::saveToCache(ScriptValuePointer options, ScriptValuePointer scope, ScriptValuePointer callback) {
559+ JS_VERIFY(options->isObject(), QString("expected options object as first parameter not: %1").arg(options->toVariant().typeName()));
551560
552− QString url = options.property("url").toString();
553− QByteArray data = qscriptvalue_cast<QByteArray>(options.property("data"));
554− QVariantMap headers = qscriptvalue_cast<QVariantMap>(options.property("headers"));
561+ QString url = options->property("url")->toString();
562+ QByteArray data = scriptvalue_cast<QByteArray>(options->property("data"));
563+ QVariantMap headers = scriptvalue_cast<QVariantMap>(options->property("headers"));
555564
556565 saveToCache(url, data, headers, scope, callback);
557566 }
558567
559−void AssetScriptingInterface::saveToCache(const QUrl& rawURL, const QByteArray& data, const QVariantMap& metadata, QScriptValue scope, QScriptValue callback) {
568+void AssetScriptingInterface::saveToCache(const QUrl& rawURL, const QByteArray& data, const QVariantMap& metadata, ScriptValuePointer scope, ScriptValuePointer callback) {
560569 QUrl url = rawURL;
561570 if (url.path().isEmpty() && !data.isEmpty()) {
562571 // generate a valid ATP URL from the data -- appending any existing fragment or querystring values