From 817bc6cd4360906a84f28ab6bbeffe0856f79f19 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Aug 2019 09:50:23 +1200 Subject: [PATCH 01/32] Assets API base functionality JSDoc --- .../src/BaseAssetScriptingInterface.h | 39 +++--- .../src/AssetScriptingInterface.h | 118 +++++++++++++----- 2 files changed, 112 insertions(+), 45 deletions(-) diff --git a/libraries/networking/src/BaseAssetScriptingInterface.h b/libraries/networking/src/BaseAssetScriptingInterface.h index 497f627421..ffe1586c71 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.h +++ b/libraries/networking/src/BaseAssetScriptingInterface.h @@ -33,51 +33,62 @@ public: public slots: /**jsdoc + * Checks whether a string is a valid path. Note: A valid path must start with a "/". * @function Assets.isValidPath - * @param {string} input - * @returns {boolean} + * @param {string} path - The path to check. + * @returns {boolean} true if the path is a valid path, false if it isn't. */ bool isValidPath(QString input) { return AssetUtils::isValidPath(input); } /**jsdoc + * Checks whether a string is a valid path and filename. Note: A valid path and filename must start with a "/" + * but must not end with a "/". * @function Assets.isValidFilePath - * @param {string} input - * @returns {boolean} + * @param {string} path - The path to check. + * @returns {boolean} true if the path is a valid file path, false if it isn't. */ bool isValidFilePath(QString input) { return AssetUtils::isValidFilePath(input); } /**jsdoc + * Gets the normalized ATP URL for a path or hash: ensures that it has "atp:/" at the start. * @function Assets.getATPUrl - * @param {string} input - * @returns {string} + * @param {string} url - The URL to normalize. + * @returns {string} The normalized ATP URL. */ QUrl getATPUrl(QString input) { return AssetUtils::getATPUrl(input); } /**jsdoc + * Gets the SHA256 hexadecimal hash portion of an asset server URL. * @function Assets.extractAssetHash - * @param {string} input - * @returns {string} + * @param {string} url - The URL to get the SHA256 hexadecimal hash from. + * @returns {string} The SHA256 hexadecimal hash portion of the URL if present and valid, "" otherwise. */ QString extractAssetHash(QString input) { return AssetUtils::extractAssetHash(input); } /**jsdoc + * Checks whether a string is a valid SHA256 hexadecimal hash, i.e., 64 hexadecimal characters. * @function Assets.isValidHash - * @param {string} input - * @returns {boolean} + * @param {string} hash - The hash to check. + * @returns {boolean} true if the hash is a valid SHA256 hexadecimal string, false if it isn't. */ bool isValidHash(QString input) { return AssetUtils::isValidHash(input); } /**jsdoc + * Calculates the SHA256 hash of given data. * @function Assets.hashData - * @param {} data - * @returns {object} + * @param {string|ArrayBuffer} data - The data to calculate the hash of. + * @returns {ArrayBuffere} The SHA256 hash of the data. */ QByteArray hashData(const QByteArray& data) { return AssetUtils::hashData(data); } /**jsdoc + * Calculates the SHA256 hash of given data, in hexadecimal format. * @function Assets.hashDataHex - * @param {} data - * @returns {string} + * @param {string|ArrayBuffer} data - The data to calculate the hash of. + * @returns {string} The SHA256 hash of the data, in hexadecimal format. + * @example Calculate the hash of some text. + * var text = "Hello world!"; + * print("Hash: " + Assets.hashDataHex(text)); */ QString hashDataHex(const QByteArray& data) { return hashData(data).toHex(); } diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 07d681ca88..29aec094cf 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -25,7 +25,9 @@ #include /**jsdoc - * The Assets API allows you to communicate with the Asset Browser. + * The Assets API provides facilities for interacting with the domain's asset server. Assets are stored in the + * asset server in files with SHA256 names. These files are mapped to user-friendly URLs of the format: + * atp:/path/filename. * @namespace Assets * * @hifi-interface @@ -41,77 +43,131 @@ public: AssetScriptingInterface(QObject* parent = nullptr); /**jsdoc - * Upload content to the connected domain's asset server. + * Uploads content to the asset server, storing it in a SHA256-named file. + *

Note: The asset server destroys any unmapped SHA256-named file at server restart. Use {@link Assets.setMapping} to + * set a path-to-hash mapping for the new file.

* @function Assets.uploadData - * @static - * @param data {string} content to upload - * @param callback {Assets~uploadDataCallback} called when upload is complete + * @param {string} data - The content to upload. + * @param {Assets~uploadDataCallback} callback - The function to call upon completion. + * @example Store a string in the asset server. + * Assets.uploadData("Hello world!", function (url, hash) { + * print("URL: " + url); // atp:0a1b...9g + * Assets.setMapping("/assetsExamples/helloWorld.txt", hash, function (error) { + * if (error) { + * print("ERROR: Could not set mapping!"); + * return; + * } + * }); + * }); */ /**jsdoc - * Called when uploadData is complete + * Called when an {@link Assets.uploadData} call is complete. * @callback Assets~uploadDataCallback - * @param {string} url - * @param {string} hash + * @param {string} url - The raw URL of the file that the content is stored in, with atp: as the scheme and + * the SHA256 hash as the filename (with no extension). + * @param {string} hash - The SHA256 hash of the content. */ Q_INVOKABLE void uploadData(QString data, QScriptValue callback); /**jsdoc - * Download data from the connected domain's asset server. + * Downloads content from the asset server, form a SHA256-named file. * @function Assets.downloadData - * @param url {string} URL of asset to download, must be ATP scheme URL. - * @param callback {Assets~downloadDataCallback} + * @param {string} url - The raw URL of asset to download: atp: followed by the assets's SHA256 hash. + * @param {Assets~downloadDataCallback} callback - The function to call upon completion. + * @example Store and retrieve a string from the asset server. + * var assetURL; + * + * // Store the string. + * Assets.uploadData("Hello world!", function (url, hash) { + * assetURL = url; + * print("url: " + assetURL); // atp:a0g89... + * Assets.setMapping("/assetsExamples/hellowWorld.txt", hash, function (error) { + * if (error) { + * print("ERROR: Could not set mapping!"); + * return; + * } + * }); + * }); + * + * // Retrieve the string. + * Script.setTimeout(function () { + * Assets.downloadData(assetURL, function (data, error) { + * print("Downloaded data: " + data); + * print("Error: " + JSON.stringify(error)); + * }); + * }, 1000); */ /**jsdoc - * Called when downloadData is complete + * Called when an {@link Assets.downloadData} call is complete. * @callback Assets~downloadDataCallback - * @param data {string} content that was downloaded + * @param {string} data - The content that was downloaded. + * @param {Assets.DownloadDataError} error - The success or failure of the download. */ - Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete); + /**jsdoc + * The success or failure of an {@link Assets.downloadData} call. + * @typedef {object} Assets.DownloadDataError + * @property {string} errorMessage - "" if the download was successful, otherwise a description of the error. + */ + Q_INVOKABLE void downloadData(QString url, QScriptValue callback); /**jsdoc - * Sets up a path to hash mapping within the connected domain's asset server + * Sets a path-to-hash mapping within the asset server. * @function Assets.setMapping - * @param path {string} - * @param hash {string} - * @param callback {Assets~setMappingCallback} + * @param {string} path - A user-friendly path for the file in the asset server, without leading "atp:". + * @param {string} hash - The hash in the asset server. + * @param {Assets~setMappingCallback} callback - The function to call upon completion. */ /**jsdoc - * Called when setMapping is complete + * Called when an {@link Assets.setMapping} call is complete. * @callback Assets~setMappingCallback - * @param {string} error + * @param {string} error - null if the path-to-hash mapping was set, otherwise a description of the error. */ Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); /**jsdoc - * Look up a path to hash mapping within the connected domain's asset server + * Gets the hash for a path within the asset server. The hash is for the unbaked or baked version of the + * asset, according to the asset server setting for the particular path. * @function Assets.getMapping - * @param path {string} - * @param callback {Assets~getMappingCallback} + * @param {string} path - The path to a file in the asset server to get the hash of. + * @param {Assets~getMappingCallback} callback - The function to call upon completion. + * @example Report the hash of an asset server item. + * var assetPath = Window.browseAssets(); + * if (assetPath) { + * var mapping = Assets.getMapping(assetPath, function (error, hash) { + * print("Asset: " + assetPath); + * print("- hash: " + hash); + * print("- error: " + error); + * }); + * } */ /**jsdoc - * Called when getMapping is complete. + * Called when an {@link Assets.getMapping} call is complete. * @callback Assets~getMappingCallback - * @param assetID {string} hash value if found, else an empty string - * @param error {string} error description if the path could not be resolved; otherwise a null value. + * @param {string} error - null if the path was found, otherwise a description of the error. + * @param {string} hash - The hash value if the path was found, "" if it wasn't. */ Q_INVOKABLE void getMapping(QString path, QScriptValue callback); /**jsdoc + * Sets whether or not to bake an asset in the asset server. * @function Assets.setBakingEnabled - * @param path {string} - * @param enabled {boolean} - * @param callback {} + * @param {string} path - The path to a file in the asset server. + * @param {boolean} enabled - true to enable baking of the asset, false to disable. + * @param {Assets~setBakingEnabledCallback} callback - The function to call upon completion. */ /**jsdoc - * Called when setBakingEnabled is complete. + * Called when an {@link Assets.setBakingEnabled} call is complete. * @callback Assets~setBakingEnabledCallback + * @param {string} error - null if baking was successfully enabled or disabled, otherwise a description of the + * error. */ + // Note: Second callback parameter not documented because it's always {}. Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback); #if (PR_BUILD || DEV_BUILD) /** * This function is purely for development purposes, and not meant for use in a - * production context. It is not a public-facing API, so it should not contain jsdoc. + * production context. It is not a public-facing API, so it should not have JSDoc. */ Q_INVOKABLE void sendFakedHandshake(); #endif From 5f3081784c7a43c5297915c4829a05f0db55346f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Aug 2019 12:36:37 +1200 Subject: [PATCH 02/32] Assets API methods that include mappings JSDoc --- .../src/BaseAssetScriptingInterface.h | 15 + .../src/AssetScriptingInterface.cpp | 15 - .../src/AssetScriptingInterface.h | 263 ++++++++++++++---- 3 files changed, 217 insertions(+), 76 deletions(-) diff --git a/libraries/networking/src/BaseAssetScriptingInterface.h b/libraries/networking/src/BaseAssetScriptingInterface.h index ffe1586c71..e2af073941 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.h +++ b/libraries/networking/src/BaseAssetScriptingInterface.h @@ -24,6 +24,21 @@ class BaseAssetScriptingInterface : public QObject { Q_OBJECT public: + + /**jsdoc + *

Types of response that {@link Assets.getAsset} and {@link Assets.loadFromCache} may provide.

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
"text"UTF-8 decoded string value.
"arraybuffer"A binary ArrayBuffer object.
"json"A parsed JSON object.
+ * @typedef {string} Assets.ResponseType + */ const QStringList RESPONSE_TYPES{ "text", "arraybuffer", "json" }; using Promise = MiniPromise::Promise; QSharedPointer assetClient(); diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index e389bd8446..fda66e0886 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -215,21 +215,6 @@ void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue sco jsVerify(false, "TODO: deleteAsset API"); } -/**jsdoc - * @typedef {string} Assets.GetOptions.ResponseType - *

Available responseType values for use with @{link Assets.getAsset} and @{link Assets.loadFromCache} configuration option.

- * - * - * - * - * - * - * - * - * - *
responseTypetypeof response value
"text"contents returned as utf-8 decoded String value
"arraybuffer"contents as a binary ArrayBuffer object
"json"contents as a parsed JSON object
- */ - void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { JS_VERIFY(options.isObject() || options.isString(), "expected request options Object or URL as first parameter"); diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 29aec094cf..31fd52f4c8 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -173,94 +173,235 @@ public: #endif /**jsdoc - * Request Asset data from the ATP Server - * @function Assets.getAsset - * @param {URL|Assets.GetOptions} options An atp: style URL, hash, or relative mapped path; or an {@link Assets.GetOptions} object with request parameters - * @param {Assets~getAssetCallback} scope A scope callback function to receive (error, results) values - * @param {function} [callback=undefined] + * Details of a callback function. + * @typedef {object} Assets.CallbackDetails + * @property {object} scope - The scope that the callback function is defined in. This object is bound to + * this when the function is called. + * @property {Assets~putAssetCallback|Assets~getAssetCallback|Assets~resolveAssetCallback} callback - The function to + * call upon completion. May be an inline function or a function identifier. If a function identifier, it must be a + * member of scope. */ /**jsdoc - * A set of properties that can be passed to {@link Assets.getAsset}. + * Source and download options for {@link Assets.getAsset}. * @typedef {object} Assets.GetOptions - * @property {string} [url] an "atp:" style URL, hash, or relative mapped path to fetch - * @property {string} [responseType=text] the desired reponse type (text | arraybuffer | json) - * @property {boolean} [decompress=false] whether to attempt gunzip decompression on the fetched data - * See: {@link Assets.putAsset} and its .compress=true option + * @property {string} url - The mapped path or hash to download. May have a leading "atp:". + * @property {Assets.ResponseType} [responseType="text"] - The desired result type. + * @property {boolean} [decompress=false] - true to gunzip decompress the downloaded data. Synonym: + * compressed. */ - /**jsdoc - * Called when Assets.getAsset is complete. + * Called when an {@link Assets.getAsset} call is complete. * @callback Assets~getAssetCallback - * @param {string} error - contains error message or null value if no error occured fetching the asset - * @param {Asset~getAssetResult} result - result object containing, on success containing asset metadata and contents + * @param {string} error - null if the content was downloaded, otherwise a description of the error. + * @param {Assets.GetResult} result - Information on and the content downloaded. */ - /**jsdoc - * Result value returned by {@link Assets.getAsset}. - * @typedef {object} Assets~getAssetResult - * @property {string} [url] the resolved "atp:" style URL for the fetched asset - * @property {string} [hash] the resolved hash for the fetched asset - * @property {string|ArrayBuffer|Object} [response] response data (possibly converted per .responseType value) - * @property {string} [responseType] response type (text | arraybuffer | json) - * @property {string} [contentType] detected asset mime-type (autodetected) - * @property {number} [byteLength] response data size in bytes - * @property {number} [decompressed] flag indicating whether data was decompressed + * Result value returned by {@link Assets.getAsset}. + * @typedef {object} Assets.GetResult + * @property {number} [byteLength] - The number of bytes in the downloaded response. + * @property {boolean} cached - + * @property {string} [contentType] - Automatically detected MIME type of the content. + * @property {boolean} [decompressed] - true if the content was decompressed, false if it wasn't. + * @property {string} [hash] - The hash for the downloaded asset. + * @property {string} [hashURL] - The ATP URL of the hash file. + * @property {string} [path] - The path for the asset, if a path was requested. Otherwise, undefined. + * @property {string|ArrayBuffer|object} [response] - The downloaded content. + * @property {Assets.ResponseType} [responseType] - The type of the downloaded content. + * @property {string} [url] - The URL of the asset requested: the path with leading "atp:" if a path was + * requested, otherwise the requested URL. + * @property {boolean} [wasRedirected] - true if the downloaded data is the baked version of the asset, + * false if it isn't baked. + */ + /**jsdoc + * Downloads content from the asset server. + * @function Assets.getAsset + * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash + * to download, optionally including a leading "atp:". + * @param {Assets~getAssetCallback} callback - The function to call upon completion. May be a function identifier or an + * inline function. + * @example Retrieve a string from the asset server. + * Assets.getAsset( + * { + * url: "/assetsExamples/helloWorld.txt", + * responseType: "text" + * }, + * function (error, result) { + * if (error) { + * print("ERROR: Data not downloaded"); + * } else { + * print("Data: " + result.response); + * } + * } + * ); + */ + /**jsdoc + * Downloads content from the asset server. + * @function Assets.getAsset + * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash + * to download, optionally including a leading "atp:". + * @param {object} scope - The scope that the callback function is defined in. This object is bound to + * this when the function is called. + * @param {Assets~getAssetCallback} callback - The function to call upon completion. May be an inline function, a function + * identifier, or the name of a function in a string. If the name of a function or a function identifier, it must be + * a member of scope. + */ + /**jsdoc + * Downloads content from the asset server. + * @function Assets.getAsset + * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash + * to download, optionally including a leading "atp:". + * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. */ - Q_INVOKABLE void getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * Upload Asset data to the ATP Server - * @function Assets.putAsset - * @param {Assets.PutOptions} options A PutOptions object with upload parameters - * @param {Assets~putAssetCallback} scope[callback] A scoped callback function invoked with (error, results) - * @param {function} [callback=undefined] - */ - - /**jsdoc - * A set of properties that can be passed to {@link Assets.putAsset}. + * Content and upload options for {@link Assets.putAsset}. * @typedef {object} Assets.PutOptions - * @property {ArrayBuffer|string} [data] byte buffer or string value representing the new asset's content - * @property {string} [path=null] ATP path mapping to automatically create (upon successful upload to hash) - * @property {boolean} [compress=false] whether to gzip compress data before uploading + * @property {string|ArrayBuffer} data - The content to upload. + * @property {string} [path] - A user-friendly path for the file in the asset server. May have a leading + * "atp:". + *

Note: The asset server destroys any unmapped SHA256-named file at server restart. Either set the mapping path + * with this property or use {@link Assets.setMapping} to set a path-to-hash mapping for the uploaded file.

+ * @property {boolean} [compress=false] - true to gzip compress the content for upload and storage, + * false to upload and store the data without gzip compression. Synonym: compressed. */ - /**jsdoc - * Called when Assets.putAsset is complete. - * @callback Assets~puttAssetCallback - * @param {string} error - contains error message (or null value if no error occured while uploading/mapping the new asset) - * @param {Asset~putAssetResult} result - result object containing error or result status of asset upload + * Called when an {@link Assets.putAsset} call is complete. + * @callback Assets~putAssetCallback + * @param {string} error - null if the content was uploaded and any path-to-hash mapping set, otherwise a + * description of the error. + * @param {Assets.PutResult} result - Information on the content uploaded. */ - /**jsdoc * Result value returned by {@link Assets.putAsset}. - * @typedef {object} Assets~putAssetResult - * @property {string} [url] the resolved "atp:" style URL for the uploaded asset (based on .path if specified, otherwise on the resulting ATP hash) - * @property {string} [path] the uploaded asset's resulting ATP path (or undefined if no path mapping was assigned) - * @property {string} [hash] the uploaded asset's resulting ATP hash - * @property {boolean} [compressed] flag indicating whether the data was compressed before upload - * @property {number} [byteLength] flag indicating final byte size of the data uploaded to the ATP server + * @typedef {object} Assets.PutResult + * @property {number} [byteLength] - The number of bytes in the hash file stored on the asset server. + * @property {boolean} [compressed] - true if the content stored is gzip compressed. + * @property {string} [contentType] - "application/gzip" if the content stored is gzip compressed. + * @property {string} [hash] - The SHA256 hash of the content. + * @property {string} [url] - The atp: URL of the content: using the path if specified, otherwise the hash. + * @property {string} [path] - The uploaded content's mapped path, if specified. + */ + /**jsdoc + * Uploads content to the assert server and sets a path-to-hash mapping. + * @function Assets.putAsset + * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the + * string is uploaded but a path-to-hash mapping is not set. + * @param {Assets~putAssetCallback} callback - The function to call upon completion. May be an inline function or a + * function identifier. + * @example Store a string in the asset server. + * Assets.putAsset( + * { + * data: "Hello world!", + * path: "/assetsExamples/helloWorld.txt" + * }, + * function (error, result) { + * if (error) { + * print("ERROR: Data not uploaded or mapping not set"); + * } else { + * print("URL: " + result.url); // atp:/assetsExamples/helloWorld.txt + * } + * } + * ); + */ + /**jsdoc + * Uploads content to the assert server and sets a path-to-hash mapping. + * @function Assets.putAsset + * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the + * string is uploaded but a path-to-hash mapping is not set. + * @param {object} scope - The scope that the callback function is defined in. This object is bound to + * this when the function is called. + * @param {Assets~getAssetCallback} callback - The function to call upon completion. May be an inline function, a function + * identifier, or the name of a function in a string. If the name of a function or a function identifier, it must be + * a member of scope. + */ + /**jsdoc + * Uploads content to the assert server and sets a path-to-hash mapping. + * @function Assets.putAsset + * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the + * string is uploaded but a path-to-hash mapping is not set. + * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. */ - Q_INVOKABLE void putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * @function Assets.deleteAsset - * @param {} options - * @param {} scope - * @param {} [callback = ""] + * Called when an {@link Assets.deleteAsset} call is complete. + *

Not implemented: This type is not implemented yet.

+ * @callback Assets~deleteAssetCallback + * @param {string} error - null if the content was deleted, otherwise a description of the error. + * @param {Assets.DeleteResult} result - Information on the content deleted. */ - - Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); - /**jsdoc - * @function Assets.resolveAsset - * @param {} options - * @param {} scope - * @param {} [callback = ""] + * Deletes content from the asset server. + *

Not implemented: This method is not implemented yet.

+ * @function Assets.deleteAsset + * @param {Assets.DeleteOptions} options - The content to delete and delete options. + * @param {object} scope - he scope that the callback function is defined in. + * @param {Assets~deleteAssetCallback} callback - The function to call upon completion. */ + Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); + /**jsdoc + * Source options for {@link Assets.resolveAsset}. + * @typedef {object} Assets.ResolveOptions + * @property {string} url - The hash or path to resolve. May have a leading "atp:". + */ + /**jsdoc + * Called when an {@link Assets.resolveAsset} call is complete. + * @callback Assets~resolveAssetCallback + * @param {string} error - null if the asset hash or path was resolved, otherwise a description of the error. + * @param {Assets.ResolveResult} result - Information on the hash or path resolved. + */ + /**jsdoc + * Result value returned by {@link Assets.resolveAsset}. + *

Note: If resolving a hash, a file of that hash need not be present on the asset server for the hash to resolve.

+ * @typedef {object} Assets.ResolveResult + * @property {string} [hash] - The hash of the asset. + * @property {string} [hashURL] - The url of the asset's hash file, with leading atp:. + * @property {string} [path] - The path to the asset. + * @property {string} [url] - The URL of the asset. + * @property {boolean} [wasRedirected] - true if the resolved data is for the baked version of the asset, + * false if it isn't. + */ + /**jsdoc + * Resolves and returns information on a hash or a path in the asset server. + * @function Assets.resolveAsset + * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying + * what to resolve. If a string, it may have a leading "atp:". + * @param {Assets~resolveAssetCallback} callback - The function to call upon completion. May be a function identifier or + * an inline function. + * @example Get the hash and URL for a path. + * Assets.resolveAsset( + * "/assetsExamples/helloWorld.txt", + * function (error, result) { + * if (error) { + * print("ERROR: " + error); + * } else { + * print("Hash: " + result.hash); + * print("URL: " + result.url); + * } + * } + * ); + */ + /**jsdoc + * Resolves and returns information on a hash or a path in the asset server. + * @function Assets.resolveAsset + * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying + * what to resolve. If a string, it may have a leading "atp:". + * @param {object} scope - The scope that the callback function is defined in. This object is bound to + * this when the function is called. + * @param {Assets~resolveAssetCallback} callback - The function to call upon completion. May be an inline function, a + * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it + * must be a member of scope. + */ + /**jsdoc + * Resolves and returns information on a hash or a path in the asset server. + * @function Assets.resolveAsset + * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying + * what to resolve. If a string, it may have a leading "atp:". + * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. + */ Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc From 11e72c638bd6449711e5358cb30bc649652f8432 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 22 Aug 2019 09:47:25 +1200 Subject: [PATCH 03/32] Assets API compression methods JSDoc --- .../src/BaseAssetScriptingInterface.h | 3 +- .../src/AssetScriptingInterface.h | 105 +++++++++++++++--- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/libraries/networking/src/BaseAssetScriptingInterface.h b/libraries/networking/src/BaseAssetScriptingInterface.h index e2af073941..f15b9acac3 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.h +++ b/libraries/networking/src/BaseAssetScriptingInterface.h @@ -26,7 +26,8 @@ class BaseAssetScriptingInterface : public QObject { public: /**jsdoc - *

Types of response that {@link Assets.getAsset} and {@link Assets.loadFromCache} may provide.

+ *

Types of response that {@link Assets.getAsset}, {@link Assets.loadFromCache}, or {@link Assets.decompressData} may + * provide.

* * * diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 31fd52f4c8..a4363fd19a 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -177,9 +177,10 @@ public: * @typedef {object} Assets.CallbackDetails * @property {object} scope - The scope that the callback function is defined in. This object is bound to * this when the function is called. - * @property {Assets~putAssetCallback|Assets~getAssetCallback|Assets~resolveAssetCallback} callback - The function to - * call upon completion. May be an inline function or a function identifier. If a function identifier, it must be a - * member of scope. + * @property {Assets~putAssetCallback|Assets~getAssetCallback|Assets~resolveAssetCallback|Assets~compressDataCallback + * |Assets~decompressDataCallback} + * callback - The function to call upon completion. May be an inline function or a function identifier. If a function + * identifier, it must be a member of scope. */ /**jsdoc @@ -405,21 +406,99 @@ public: Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * @function Assets.decompressData - * @param {} options - * @param {} scope - * @param {} [callback = ""] + * Content and decompression options for {@link Assets.decompressData}. + * @typedef {object} Assets.DecompressOptions + * @property {ArrayBuffer} data - The data to decompress. + * @property {Assets.ResponseType} [responseType=text] - The type of data to return. + */ + /**jsdoc + * Called when an {@link Assets.decompressData} call is complete. + * @callback Assets~decompressDataCallback + * @param {string} error - null if the data was successfully compressed, otherwise a description of the error. + * @param {Assets.DecompressResult} result - Information on and the decompressed data. + */ + /**jsdoc + * Result value returned by {@link Assets.decompressData}. + * @typedef {object} Assets.DecompressResult + * @property {number} [byteLength] - The number of bytes in the decompressed data. + * @property {string} [contentType] - The MIME type of the decompressed data. + * @property {boolean} [decompressed] - true if the data is decompressed. + * @property {string|ArrayBuffer|object} [response] - The decompressed data. + * @property {Assets.ResponseType} [responseType] - The type of the response. + */ + /**jsdoc + * Decompresses data in memory using gunzip. + * @function Assets.decompressData + * @param {Assets.DecompressOptions} source - What to decompress and decompression options. + * @param {Assets~decompressDataCallback} callback - The function to call upon completion. May be a function identifier or + * an inline function. + */ + /**jsdoc + * Decompresses data in memory using gunzip. + * @function Assets.decompressData + * @param {Assets.DecompressOptions} source - What to decompress and decompression options. + * @param {object} scope - The scope that the callback function is defined in. This object is bound to + * this when the function is called. + * @param {Assets~decompressDataCallback} callback - The function to call upon completion. May be an inline function, a + * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it + * must be a member of scope. + */ + /**jsdoc + * Decompresses data in memory using gunzip. + * @function Assets.decompressData + * @param {Assets.DecompressOptions} source - What to decompress and decompression options. + * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. */ - Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * @function Assets.compressData - * @param {} options - * @param {} scope - * @param {} [callback = ""] + * Content and compression options for {@link Assets.compressData}. + * @typedef {object} Assets.CompressOptions + * @property {ArrayBuffer|string} data - The data to compress. + * @property {number} level - The compression level, range -19. -1 means + * use the default gzip compression level, 0 means no compression, and 9 means maximum + * compression. + */ + /**jsdoc + * Called when an {@link Assets.compressData} call is complete. + * @callback Assets~compressDataCallback + * @param {string} error - null if the data was successfully compressed, otherwise a description of the error. + * @param {Assets.CompressResult} result - Information on and the compressed data. + */ + /**jsdoc + * Result value returned by {@link Assets.compressData}. + * @typedef {object} Assets.CompressResult + * @property {boolean} [compressed] - true if the data is compressed. + * @property {number} [byteLength] - The number of bytes in the compressed data. + * @property {string} [contentType] - The MIME type of the compressed data, i.e., "application/gzip". + * @property {ArrayBuffer} [data] - The compressed data. + */ + /**jsdoc + * Compresses data in memory using gzip. + * @function Assets.compressData + * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compression options. If an ArrayBuffer + * or a string, the data to compress. + * @param {Assets~compressDataCallback} callback - The function to call upon completion. May be a function identifier or an + * inline function. + */ + /**jsdoc + * Compresses data in memory using gzip. + * @function Assets.compressData + * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compression options. If an ArrayBuffer + * or a string, the data to compress. + * @param {object} scope - The scope that the callback function is defined in. This object is bound to + * this when the function is called. + * @param {Assets~compressDataCallback} callback - The function to call upon completion. May be an inline function, a + * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it + * must be a member of scope. + */ + /**jsdoc + * Compresses data in memory using gzip. + * @function Assets.compressData + * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compressopn options. If an ArrayBuffer + * or a string, the data to compress. + * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. */ - Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc From 942347158a482e1366fc34f5e3f058a722f25797 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 22 Aug 2019 10:16:56 +1200 Subject: [PATCH 04/32] Collapse some function signatures' JSDoc --- .../src/AssetScriptingInterface.h | 54 ++++--------------- 1 file changed, 10 insertions(+), 44 deletions(-) diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index a4363fd19a..bb98f32131 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -219,8 +219,8 @@ public: * @function Assets.getAsset * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash * to download, optionally including a leading "atp:". - * @param {Assets~getAssetCallback} callback - The function to call upon completion. May be a function identifier or an - * inline function. + * @param {Assets.CallbackDetails|Assets~getAssetCallback} callback - The function to call upon completion. May be a + * function identifier or an inline function. * @example * Assets.getAsset( * { @@ -247,13 +247,6 @@ public: * identifier, or the name of a function in a string. If the name of a function or a function identifier, it must be * a member of scope. */ - /**jsdoc - * Downloads content from the asset server. - * @function Assets.getAsset - * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash - * to download, optionally including a leading "atp:". - * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. - */ Q_INVOKABLE void getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc @@ -289,8 +282,8 @@ public: * @function Assets.putAsset * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the * string is uploaded but a path-to-hash mapping is not set. - * @param {Assets~putAssetCallback} callback - The function to call upon completion. May be an inline function or a - * function identifier. + * @param {Assets.CallbackDetails|Assets~putAssetCallback} callback - The function to call upon completion. May be an + * inline function or a function identifier. * @example * Assets.putAsset( * { @@ -317,13 +310,6 @@ public: * identifier, or the name of a function in a string. If the name of a function or a function identifier, it must be * a member of scope. */ - /**jsdoc - * Uploads content to the assert server and sets a path-to-hash mapping. - * @function Assets.putAsset - * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the - * string is uploaded but a path-to-hash mapping is not set. - * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. - */ Q_INVOKABLE void putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc @@ -370,8 +356,8 @@ public: * @function Assets.resolveAsset * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying * what to resolve. If a string, it may have a leading "atp:". - * @param {Assets~resolveAssetCallback} callback - The function to call upon completion. May be a function identifier or - * an inline function. + * @param {Assets.CallbackDetails|Assets~resolveAssetCallback} callback - The function to call upon completion. May be a + * function identifier or an inline function. * @example * Assets.resolveAsset( * "/assetsExamples/helloWorld.txt", @@ -396,13 +382,6 @@ public: * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it * must be a member of scope. */ - /**jsdoc - * Resolves and returns information on a hash or a path in the asset server. - * @function Assets.resolveAsset - * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying - * what to resolve. If a string, it may have a leading "atp:". - * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. - */ Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc @@ -430,8 +409,8 @@ public: * Decompresses data in memory using gunzip. * @function Assets.decompressData * @param {Assets.DecompressOptions} source - What to decompress and decompression options. - * @param {Assets~decompressDataCallback} callback - The function to call upon completion. May be a function identifier or - * an inline function. + * @param {Assets.CallbackDetails|Assets~decompressDataCallback} callback - The function to call upon completion. May be a + * function identifier or an inline function. */ /**jsdoc * Decompresses data in memory using gunzip. @@ -443,12 +422,6 @@ public: * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it * must be a member of scope. */ - /**jsdoc - * Decompresses data in memory using gunzip. - * @function Assets.decompressData - * @param {Assets.DecompressOptions} source - What to decompress and decompression options. - * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. - */ Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc @@ -478,8 +451,8 @@ public: * @function Assets.compressData * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compression options. If an ArrayBuffer * or a string, the data to compress. - * @param {Assets~compressDataCallback} callback - The function to call upon completion. May be a function identifier or an - * inline function. + * @param {Assets.CallbackDetails|Assets~compressDataCallback} callback - The function to call upon completion. May be a + * function identifier or an inline function. */ /**jsdoc * Compresses data in memory using gzip. @@ -492,13 +465,6 @@ public: * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it * must be a member of scope. */ - /**jsdoc - * Compresses data in memory using gzip. - * @function Assets.compressData - * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compressopn options. If an ArrayBuffer - * or a string, the data to compress. - * @param {Assets.CallbackDetails} callbackDetails - Details of the function to call upon completion. - */ Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc From 2ad1d7a2a281558c130599a9d8ddc834ac27594e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 22 Aug 2019 10:27:17 +1200 Subject: [PATCH 05/32] Fix typos --- libraries/networking/src/BaseAssetScriptingInterface.h | 4 ++-- libraries/script-engine/src/AssetScriptingInterface.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/BaseAssetScriptingInterface.h b/libraries/networking/src/BaseAssetScriptingInterface.h index f15b9acac3..ac2c211343 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.h +++ b/libraries/networking/src/BaseAssetScriptingInterface.h @@ -66,7 +66,7 @@ public slots: bool isValidFilePath(QString input) { return AssetUtils::isValidFilePath(input); } /**jsdoc - * Gets the normalized ATP URL for a path or hash: ensures that it has "atp:/" at the start. + * Gets the normalized ATP URL for a path or hash: ensures that it has "atp:" at the start. * @function Assets.getATPUrl * @param {string} url - The URL to normalize. * @returns {string} The normalized ATP URL. @@ -93,7 +93,7 @@ public slots: * Calculates the SHA256 hash of given data. * @function Assets.hashData * @param {string|ArrayBuffer} data - The data to calculate the hash of. - * @returns {ArrayBuffere} The SHA256 hash of the data. + * @returns {ArrayBuffer} The SHA256 hash of the data. */ QByteArray hashData(const QByteArray& data) { return AssetUtils::hashData(data); } diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index bb98f32131..a93114275a 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -324,7 +324,7 @@ public: *

Not implemented: This method is not implemented yet.

* @function Assets.deleteAsset * @param {Assets.DeleteOptions} options - The content to delete and delete options. - * @param {object} scope - he scope that the callback function is defined in. + * @param {object} scope - The scope that the callback function is defined in. * @param {Assets~deleteAssetCallback} callback - The function to call upon completion. */ Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); From 8820d1d76bc0b088101576df23c3756b383bb562 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 4 Sep 2019 13:48:06 +1200 Subject: [PATCH 06/32] Assets API cache methods --- libraries/networking/src/AssetClient.cpp | 39 ++++ .../src/BaseAssetScriptingInterface.cpp | 11 ++ .../src/AssetScriptingInterface.cpp | 17 ++ .../src/AssetScriptingInterface.h | 179 +++++++++++++++--- 4 files changed, 216 insertions(+), 30 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index b9a3e6f61e..0ba21e93cd 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -85,6 +85,13 @@ namespace { const QString& CACHE_ERROR_MESSAGE{ "AssetClient::Error: %1 %2" }; } +/**jsdoc + * Cache status value returned by {@link Assets.getCacheStatus}. + * @typedef {object} Assets.GetCacheStatusResult + * @property {string} cacheDirectory - The path of the cache directory. + * @property {number} cacheSize - The current cache size, in bytes. + * @property {number} maximumCacheSize - The maximum cache size, in bytes. + */ MiniPromise::Promise AssetClient::cacheInfoRequestAsync(MiniPromise::Promise deferred) { if (!deferred) { deferred = makePromise(__FUNCTION__); // create on caller's thread @@ -106,6 +113,20 @@ MiniPromise::Promise AssetClient::cacheInfoRequestAsync(MiniPromise::Promise def return deferred; } +/**jsdoc + * Information on an asset in the cache. Value returned by {@link Assets.queryCacheMeta} and included in the data returned by + * {@link Assets.loadFromCache}. + * @typedef {object} Assets.CacheItemMetaData + * @property {object} [attributes] - The attributes that are stored with this cache item. Not used. + * @property {Date} [expirationDate] - The date and time when the meta data expires. An invalid date means "never expires". + * @property {boolean} isValid - true if the item specified in the URL is in the cache, false if + * it isn't. + * @property {Date} [lastModified] - The date and time when the meta data was last modified. + * @property {object} [rawHeaders] - The raw headers that are set in the meta data. Not used. + * @property {boolean} [saveToDisk] - true if the cache item is allowed to be store on disk, + * false if it isn't. + * @property {string} [url|metaDataURL] - The ATP URL of the cached item. + */ MiniPromise::Promise AssetClient::queryCacheMetaAsync(const QUrl& url, MiniPromise::Promise deferred) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "queryCacheMetaAsync", Q_ARG(const QUrl&, url), Q_ARG(MiniPromise::Promise, deferred)); @@ -202,6 +223,24 @@ namespace { } } +/**jsdoc + * Last-modified and expiry times for a cache item. + * @typedef {object} Assets.SaveToCacheHeaders + * @property {string} [last-modified] - The date and time the cache value was last modified, in the format: + * "ddd, dd MMM yyyy HH:mm:ss". The default value is the current date and time. + * @property {string} [expires] - The date and time the cache value expires, in the format: + * "ddd, dd MMM yyyy HH:mm:ss". The default value is an invalid date, representing "never expires". + */ +/**jsdoc + * Information on saving asset data to the cache with {@link Assets.saveToCache}. + * @typedef {object} Assets.SaveToCacheResult + * @property {number} [byteLength] - The size of the cached data, in bytes. + * @property {Date} [expirationDate] - The date and time that the cache item expires. An invalid date means "never expires". + * @property {Date} [lastModified] - The date and time that the cache item was last modified. + * @property {string} [metaDataURL] - The URL associated with the cache item. + * @property {boolean} [success] - true if the save to cache request was successful. + * @property {string} [url] - The URL associated with the cache item. + */ MiniPromise::Promise AssetClient::saveToCacheAsync(const QUrl& url, const QByteArray& data, const QVariantMap& headers, MiniPromise::Promise deferred) { if (!deferred) { deferred = makePromise(__FUNCTION__); // create on caller's thread diff --git a/libraries/networking/src/BaseAssetScriptingInterface.cpp b/libraries/networking/src/BaseAssetScriptingInterface.cpp index b231339e51..2a98dbf3c3 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.cpp +++ b/libraries/networking/src/BaseAssetScriptingInterface.cpp @@ -68,6 +68,17 @@ Promise BaseAssetScriptingInterface::queryCacheMeta(const QUrl& url) { return assetClient()->queryCacheMetaAsync(url, makePromise(__FUNCTION__)); } +/**jsdoc + * Data and information returned by {@link Assets.loadFromCache}. + * @typedef {object} Assets.LoadFromCacheResult + * @property {number} [byteLength] - The number of bytes in the retrieved data. + * @property {string} [contentType] - The automatically detected MIME type of the content. + * @property {ArrayBuffer} data - The data bytes. + * @property {Assets.CacheItemMetaData} metadata - Information on the cache item. + * @property {string|object|ArrayBuffer} [response] - The content of the response. + * @property {Assets.ResponseType} responseType - The type of the content in response. + * @property {string} url - The URL of the cache item. + */ Promise BaseAssetScriptingInterface::loadFromCache(const QUrl& url, bool decompress, const QString& responseType) { QVariantMap metaData = { { "_type", "cache" }, diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index fda66e0886..c440aab830 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -362,6 +362,15 @@ void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue jsPromiseReady(Parent::queryCacheMeta(url), scope, callback); } +/**jsdoc + * Source and retrieval options for {@link Assets.loadFromCache}. + * @typedef {object} Assets.LoadFromCacheOptions + * @property {string} url - The URL of the asset to load from cache. Must start with "atp:" or + * "cache:". + * @property {Assets.ResponseType} [responseType=text] - The desired result type. + * @property {boolean} [decompress=false] - true to gunzip decompress the cached data. Synonym: + * compressed. + */ void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback) { QString url, responseType; bool decompress = false; @@ -396,6 +405,14 @@ bool AssetScriptingInterface::canWriteCacheValue(const QUrl& url) { return true; } +/**jsdoc + * The data to save to the cache and cache options for {@link Assets.saveToCache}. + * @typedef {object} Assets.SaveToCacheOptions + * @property {string|ArrayBuffer} data - The data to save to the cache. + * @property {Assets.SaveToCacheHeaders} [headers] - The last-modified and expiry times for the cache item. + * @property {string} [url] - The URL to associate with the cache item. Must start with "atp:" or + * "cache:". If not specified, the URL is "atp:" followed by the SHA256 hash of the content. + */ void AssetScriptingInterface::saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback) { JS_VERIFY(options.isObject(), QString("expected options object as first parameter not: %1").arg(options.toVariant().typeName())); diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index a93114275a..8a0ec27817 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -468,66 +468,185 @@ public: Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc + * Initializes the local cache if it isn't already initialized. * @function Assets.initializeCache - * @returns {boolean} + * @returns {boolean} true if the local cache is initialized, false if it isn't. */ - Q_INVOKABLE bool initializeCache() { return Parent::initializeCache(); } /**jsdoc + * Checks whether the script can write to the local cache. * @function Assets.canWriteCacheValue - * @param {string} url - * @returns {boolean} + * @param {string} url - Not used. + * @returns {boolean} true if the script is an Interface, avatar, or assignment client script, + * false if the script is a client entity or server entity script. + * @example
+ * print("Can write to cache: " + Assets.canWriteCacheValue(null)); */ - Q_INVOKABLE bool canWriteCacheValue(const QUrl& url); /**jsdoc - * @function Assets.getCacheStatus - * @param {} scope - * @param {} [callback=undefined] + * Called when a {@link Assets.getCacheStatus} call is complete. + * @callback Assets~getCacheStatusCallback + * @param {string} error - null if the cache status was retrieved without error, otherwise a description of + * the error. + * @param {Assets.GetCacheStatusResult} status - Details of the current cache status. + */ + /**jsdoc + * Gets the current cache status. + * @function Assets.getCacheStatus + * @param {object|Assets.CallbackDetails|Assets~getCacheStatusCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~getCacheStatusCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

+ * @example + * Assets.getCacheStatus(function (error, status) { + * print("Cache status"); + * print("- Error: " + error); + * print("- Status: " + JSON.stringify(status)); + * }); */ - Q_INVOKABLE void getCacheStatus(QScriptValue scope, QScriptValue callback = QScriptValue()) { jsPromiseReady(Parent::getCacheStatus(), scope, callback); } /**jsdoc - * @function Assets.queryCacheMeta - * @param {} options - * @param {} scope - * @param {} [callback=undefined] + * Called when {@link Assets.queryCacheMeta} is complete. + * @callback Assets~queryCacheMetaCallback + * @param {string} error - null if the URL has a valid cache entry, otherwise a description of the error. + * @param {Assets.CacheItemMetaData} queryResult - Information on an asset in the cache. + */ + /**jsdoc + * Gets information about the status of an asset in the cache. + * @function Assets.queryCacheMeta + * @param {string|object} path - The URL of the cached asset to get information on. If an object then path.url + * is used. Must start with "atp:" or "cache:". + * @param {object|Assets.CallbackDetails|Assets~queryCacheMetaCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~queryCacheMetaCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

+ * @example + * Assets.queryCacheMeta( + * "cache:/cacheExample/helloCache.txt", + * function (error, result) { + * if (error) { + * print("Error: " + error); + * } else { + * print("Success:"); + * print("- URL: " + result.url); + * print("- isValid: " + result.isValid); + * print("- saveToDisk: " + result.saveToDisk); + * print("- expirationDate: " + result.expirationDate); + * } + * } + * ); */ - Q_INVOKABLE void queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * @function Assets.loadFromCache - * @param {} options - * @param {} scope - * @param {} [callback=undefined] + * Called when an {@link Assets.loadFromCache} call is complete. + * @callback Assets~loadFromCacheCallback + * @param {string} error - null if the cache item was successfully retrieved, otherwise a description of the + * error. + * @param {Assets.LoadFromCacheResult} result - Information on and the retrieved data. + */ + /**jsdoc + * Retrieves data from the cache directly, without downloading it. + * @function Assets.loadFromCache + * @param {string|Assets.LoadFromCacheOptions} options - The URL of the asset to load from the cache if a string, otherwise + * an object specifying the asset to load from the cache and load options. The URL must start with "atp:" + * or "cache:". + * @param {object|Assets.CallbackDetails|Assets~loadFromCacheCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~loadFromCacheCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

+ * @example + * Assets.loadFromCache( + * "cache:/cacheExample/helloCache.txt", + * function (error, result) { + * if (error) { + * print("Error: " + error); + * } else { + * print("Success:"); + * print("- Response: " + result.response); + * print("- Content type: " + result.contentType); + * print("- Number of bytes: " + result.byteLength); + * print("- Bytes: " + [].slice.call(new Uint8Array(result.data), 0, result.byteLength)); + * print("- URL: " + result.url); + * } + * } + * ); */ - Q_INVOKABLE void loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * @function Assets.saveToCache - * @param {} options - * @param {} scope - * @param {} [callback=undefined] + * Called when an {@link Assets.saveToCache} call is complete. + * @callback Assets~saveToCacheCallback + * @param {string} error - null if the asset data was successfully saved to the cache, otherwise a description + * of the error. + * @param {Assets.SaveToCacheResult} result - Information on the cached data. + */ + /**jsdoc + * Saves asset data to the cache directly, without downloading it from a URL. + *

Note: Can only be used in Interface, avatar, and assignment client scripts.

+ * @function Assets.saveToCache + * @param {Assets.SaveToCacheOptions} options - The data to save to the cache and cache options. + * @param {object|Assets.CallbackDetails|Assets~saveToCacheCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~saveToCacheCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

+ * @example + * Assets.saveToCache( + * { + * url: "cache:/cacheExample/helloCache.txt", + * data: "Hello cache" + * }, + * function (error, result) { + * if (error) { + * print("Error: " + error); + * } else { + * print("Success:"); + * print("- Bytes: " + result.byteLength); + * print("- URL: " + result.url); + * } + * } + * ); */ - Q_INVOKABLE void saveToCache(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc + * Saves asset data to the cache directly, without downloading it from a URL. + *

Note: Can only be used in Interface, avatar, and assignment client scripts.

* @function Assets.saveToCache - * @param {} url - * @param {} data - * @param {} metadata - * @param {} scope - * @param {} [callback=undefined] + * @param {string} url - The URL to associate with the cache item. Must start with "atp:" or + * "cache:". + * @param {string|ArrayBuffer} data - The data to save to the cache. + * @param {Assets.SaveToCacheHeaders} headers - The last-modified and expiry times for the cache item. + * @param {object|Assets.CallbackDetails|Assets~saveToCacheCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~saveToCacheCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

*/ - Q_INVOKABLE void saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata, QScriptValue scope, QScriptValue callback = QScriptValue()); protected: From 9457aed631b3dfdefe9deb8b216d151f91fe581e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 4 Sep 2019 15:43:18 +1200 Subject: [PATCH 07/32] Tidying and polishing Assets JSDoc --- libraries/networking/src/AssetClient.cpp | 6 +- .../src/BaseAssetScriptingInterface.h | 4 +- .../src/AssetScriptingInterface.cpp | 114 ++++++- .../src/AssetScriptingInterface.h | 288 ++++++------------ 4 files changed, 202 insertions(+), 210 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 0ba21e93cd..44f42caec2 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -226,10 +226,10 @@ namespace { /**jsdoc * Last-modified and expiry times for a cache item. * @typedef {object} Assets.SaveToCacheHeaders - * @property {string} [last-modified] - The date and time the cache value was last modified, in the format: - * "ddd, dd MMM yyyy HH:mm:ss". The default value is the current date and time. - * @property {string} [expires] - The date and time the cache value expires, in the format: + * @property {string} [expires] - The date and time the cache value expires, in the format: * "ddd, dd MMM yyyy HH:mm:ss". The default value is an invalid date, representing "never expires". + * @property {string} [last-modified] - The date and time the cache value was last modified, in the format: + * "ddd, dd MMM yyyy HH:mm:ss". The default value is the current date and time. */ /**jsdoc * Information on saving asset data to the cache with {@link Assets.saveToCache}. diff --git a/libraries/networking/src/BaseAssetScriptingInterface.h b/libraries/networking/src/BaseAssetScriptingInterface.h index ac2c211343..7d118e1979 100644 --- a/libraries/networking/src/BaseAssetScriptingInterface.h +++ b/libraries/networking/src/BaseAssetScriptingInterface.h @@ -26,16 +26,16 @@ class BaseAssetScriptingInterface : public QObject { public: /**jsdoc - *

Types of response that {@link Assets.getAsset}, {@link Assets.loadFromCache}, or {@link Assets.decompressData} may + *

Types of response that {@link Assets.decompressData}, {@link Assets.getAsset}, or {@link Assets.loadFromCache} may * provide.

*
ValueDescription
Retrieve a string from the asset server.Store a string in the asset server.Get the hash and URL for a path.Report whether the script can write to the cache.Report the cache status.Report details of a string store in the cache.Retrieve a string from the cache.Save a string in the cache.
* * * * - * * * + * * *
ValueDescription
"text"UTF-8 decoded string value.
"arraybuffer"A binary ArrayBuffer object.
"json"A parsed JSON object.
"text"UTF-8 decoded string value.
* @typedef {string} Assets.ResponseType diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index c440aab830..db5776663c 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -81,6 +81,11 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu setMappingRequest->start(); } +/**jsdoc + * The success or failure of an {@link Assets.downloadData} call. + * @typedef {object} Assets.DownloadDataError + * @property {string} errorMessage - "" if the download was successful, otherwise a description of the error. + */ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { // FIXME: historically this API method failed silently when given a non-atp prefixed // urlString (or if the AssetRequest failed). @@ -215,6 +220,32 @@ void AssetScriptingInterface::deleteAsset(QScriptValue options, QScriptValue sco jsVerify(false, "TODO: deleteAsset API"); } +/**jsdoc + * Source and download options for {@link Assets.getAsset}. + * @typedef {object} Assets.GetOptions + * @property {boolean} [decompress=false] - true to gunzip decompress the downloaded data. Synonym: + * compressed. + * @property {Assets.ResponseType} [responseType="text"] - The desired result type. + * @property {string} url - The mapped path or hash to download. May have a leading "atp:". + */ +/**jsdoc + * Result value returned by {@link Assets.getAsset}. + * @typedef {object} Assets.GetResult + * @property {number} [byteLength] - The number of bytes in the downloaded content in response. + * @property {boolean} cached - true if the item was retrieved from the cache, false if it was + * downloaded. + * @property {string} [contentType] - The automatically detected MIME type of the content. + * @property {boolean} [decompressed] - true if the content was decompressed, false if it wasn't. + * @property {string} [hash] - The hash for the downloaded asset. + * @property {string} [hashURL] - The ATP URL of the hash file. + * @property {string} [path] - The path for the asset, if a path was requested. Otherwise, undefined. + * @property {string|object|ArrayBuffer} [response] - The downloaded content. + * @property {Assets.ResponseType} [responseType] - The type of the downloaded content in response. + * @property {string} [url] - The URL of the asset requested: the path with leading "atp:" if a path was + * requested, otherwise the requested URL. + * @property {boolean} [wasRedirected] - true if the downloaded data is the baked version of the asset, + * false if it isn't baked. + */ void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { JS_VERIFY(options.isObject() || options.isString(), "expected request options Object or URL as first parameter"); @@ -262,6 +293,22 @@ void AssetScriptingInterface::getAsset(QScriptValue options, QScriptValue scope, } } +/**jsdoc + * Source options for {@link Assets.resolveAsset}. + * @typedef {object} Assets.ResolveOptions + * @property {string} url - The hash or path to resolve. May have a leading "atp:". + */ +/**jsdoc + * Result value returned by {@link Assets.resolveAsset}. + *

Note: If resolving a hash, a file of that hash need not be present on the asset server for the hash to resolve.

+ * @typedef {object} Assets.ResolveResult + * @property {string} [hash] - The hash of the asset. + * @property {string} [hashURL] - The url of the asset's hash file, with leading atp:. + * @property {string} [path] - The path to the asset. + * @property {string} [url] - The URL of the asset. + * @property {boolean} [wasRedirected] - true if the resolved data is for the baked version of the asset, + * false if it isn't. + */ void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { const QString& URL{ "url" }; @@ -274,6 +321,21 @@ void AssetScriptingInterface::resolveAsset(QScriptValue options, QScriptValue sc jsPromiseReady(getAssetInfo(asset), scope, callback); } +/**jsdoc + * Content and decompression options for {@link Assets.decompressData}. + * @typedef {object} Assets.DecompressOptions + * @property {ArrayBuffer} data - The data to decompress. + * @property {Assets.ResponseType} [responseType=text] - The type of decompressed data to return. + */ +/**jsdoc + * Result value returned by {@link Assets.decompressData}. + * @typedef {object} Assets.DecompressResult + * @property {number} [byteLength] - The number of bytes in the decompressed data. + * @property {string} [contentType] - The MIME type of the decompressed data. + * @property {boolean} [decompressed] - true if the data is decompressed. + * @property {string|object|ArrayBuffer} [response] - The decompressed data. + * @property {Assets.ResponseType} [responseType] - The type of the decompressed data in response. + */ void AssetScriptingInterface::decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback) { auto data = options.property("data"); QByteArray dataByteArray = qscriptvalue_cast(data); @@ -298,6 +360,23 @@ namespace { const int32_t DEFAULT_GZIP_COMPRESSION_LEVEL = -1; const int32_t MAX_GZIP_COMPRESSION_LEVEL = 9; } + +/**jsdoc + * Content and compression options for {@link Assets.compressData}. + * @typedef {object} Assets.CompressOptions + * @property {string|ArrayBuffer} data - The data to compress. + * @property {number} level - The compression level, range -19. -1 means + * use the default gzip compression level, 0 means no compression, and 9 means maximum + * compression. + */ +/**jsdoc + * Result value returned by {@link Assets.compressData}. + * @typedef {object} Assets.CompressResult + * @property {number} [byteLength] - The number of bytes in the compressed data. + * @property {boolean} [compressed] - true if the data is compressed. + * @property {string} [contentType] - The MIME type of the compressed data, i.e., "application/gzip". + * @property {ArrayBuffer} [data] - The compressed data. + */ void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue scope, QScriptValue callback) { auto data = options.property("data").isValid() ? options.property("data") : options; QByteArray dataByteArray = data.isString() ? data.toString().toUtf8() : qscriptvalue_cast(data); @@ -306,6 +385,27 @@ void AssetScriptingInterface::compressData(QScriptValue options, QScriptValue sc jsPromiseReady(compressBytes(dataByteArray, level), scope, callback); } +/**jsdoc + * Content and upload options for {@link Assets.putAsset}. + * @typedef {object} Assets.PutOptions + * @property {boolean} [compress=false] - true to gzip compress the content for upload and storage, + * false to upload and store the data without gzip compression. Synonym: compressed. + * @property {string|ArrayBuffer} data - The content to upload. + * @property {string} [path] - A user-friendly path for the file in the asset server. May have a leading + * "atp:". IF not specified, no path-to-hash mapping is set. + *

Note: The asset server destroys any unmapped SHA256-named file at server restart. Either set the mapping path + * with this property or use {@link Assets.setMapping} to set a path-to-hash mapping for the uploaded file.

+ */ +/**jsdoc + * Result value returned by {@link Assets.putAsset}. + * @typedef {object} Assets.PutResult + * @property {number} [byteLength] - The number of bytes in the hash file stored on the asset server. + * @property {boolean} [compressed] - true if the content stored is gzip compressed. + * @property {string} [contentType] - "application/gzip" if the content stored is gzip compressed. + * @property {string} [hash] - The SHA256 hash of the content. + * @property {string} [url] - The atp: URL of the content: using the path if specified, otherwise the hash. + * @property {string} [path] - The uploaded content's mapped path, if specified. + */ void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback) { auto compress = options.property("compress").toBool() || options.property("compressed").toBool(); auto data = options.isObject() ? options.property("data") : options; @@ -356,6 +456,12 @@ void AssetScriptingInterface::putAsset(QScriptValue options, QScriptValue scope, } } +/**jsdoc + * Source for {@link Assets.queryCacheMeta}. + * @typedef {object} Assets.QueryCacheMetaOptions + * @property {string} url - The URL of the cached asset to get information on. Must start with "atp:" or + * "cache:". + */ void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue scope, QScriptValue callback) { QString url = options.isString() ? options.toString() : options.property("url").toString(); JS_VERIFY(QUrl(url).isValid(), QString("Invalid URL '%1'").arg(url)); @@ -365,11 +471,11 @@ void AssetScriptingInterface::queryCacheMeta(QScriptValue options, QScriptValue /**jsdoc * Source and retrieval options for {@link Assets.loadFromCache}. * @typedef {object} Assets.LoadFromCacheOptions - * @property {string} url - The URL of the asset to load from cache. Must start with "atp:" or - * "cache:". - * @property {Assets.ResponseType} [responseType=text] - The desired result type. - * @property {boolean} [decompress=false] - true to gunzip decompress the cached data. Synonym: + * @property {boolean} [decompress=false] - true to gunzip decompress the cached data. Synonym: * compressed. + * @property {Assets.ResponseType} [responseType=text] - The desired result type. + * @property {string} url - The URL of the asset to load from cache. Must start with "atp:" or + * "cache:". */ void AssetScriptingInterface::loadFromCache(QScriptValue options, QScriptValue scope, QScriptValue callback) { QString url, responseType; diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 8a0ec27817..c4c7940a80 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -25,9 +25,15 @@ #include /**jsdoc - * The Assets API provides facilities for interacting with the domain's asset server. Assets are stored in the - * asset server in files with SHA256 names. These files are mapped to user-friendly URLs of the format: - * atp:/path/filename. + * The Assets API provides facilities for interacting with the domain's asset server and the client cache. + * cache. + *

Assets are stored in the asset server in files with SHA256 names. These files are mapped to user-friendly URLs of the + * format: atp:/path/filename. The assets may optionally be baked, in which case a request for the original + * unbaked version of the asset is automatically redirected to the baked version. The asset data may optionally be stored as + * compressed.

+ *

The client cache can be access directly, using "atp:" "cache:" URLs. Interface, avatar, and + * assignment client scripts can write to the cache. All script types can read from the cache.

+ * * @namespace Assets * * @hifi-interface @@ -69,6 +75,12 @@ public: */ Q_INVOKABLE void uploadData(QString data, QScriptValue callback); + /**jsdoc + * Called when an {@link Assets.downloadData} call is complete. + * @callback Assets~downloadDataCallback + * @param {string} data - The content that was downloaded. + * @param {Assets.DownloadDataError} error - The success or failure of the download. + */ /**jsdoc * Downloads content from the asset server, form a SHA256-named file. * @function Assets.downloadData @@ -97,19 +109,13 @@ public: * }); * }, 1000); */ - /**jsdoc - * Called when an {@link Assets.downloadData} call is complete. - * @callback Assets~downloadDataCallback - * @param {string} data - The content that was downloaded. - * @param {Assets.DownloadDataError} error - The success or failure of the download. - */ - /**jsdoc - * The success or failure of an {@link Assets.downloadData} call. - * @typedef {object} Assets.DownloadDataError - * @property {string} errorMessage - "" if the download was successful, otherwise a description of the error. - */ Q_INVOKABLE void downloadData(QString url, QScriptValue callback); + /**jsdoc + * Called when an {@link Assets.setMapping} call is complete. + * @callback Assets~setMappingCallback + * @param {string} error - null if the path-to-hash mapping was set, otherwise a description of the error. + */ /**jsdoc * Sets a path-to-hash mapping within the asset server. * @function Assets.setMapping @@ -117,13 +123,14 @@ public: * @param {string} hash - The hash in the asset server. * @param {Assets~setMappingCallback} callback - The function to call upon completion. */ - /**jsdoc - * Called when an {@link Assets.setMapping} call is complete. - * @callback Assets~setMappingCallback - * @param {string} error - null if the path-to-hash mapping was set, otherwise a description of the error. - */ Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); + /**jsdoc + * Called when an {@link Assets.getMapping} call is complete. + * @callback Assets~getMappingCallback + * @param {string} error - null if the path was found, otherwise a description of the error. + * @param {string} hash - The hash value if the path was found, "" if it wasn't. + */ /**jsdoc * Gets the hash for a path within the asset server. The hash is for the unbaked or baked version of the * asset, according to the asset server setting for the particular path. @@ -140,14 +147,14 @@ public: * }); * } */ - /**jsdoc - * Called when an {@link Assets.getMapping} call is complete. - * @callback Assets~getMappingCallback - * @param {string} error - null if the path was found, otherwise a description of the error. - * @param {string} hash - The hash value if the path was found, "" if it wasn't. - */ Q_INVOKABLE void getMapping(QString path, QScriptValue callback); + /**jsdoc + * Called when an {@link Assets.setBakingEnabled} call is complete. + * @callback Assets~setBakingEnabledCallback + * @param {string} error - null if baking was successfully enabled or disabled, otherwise a description of the + * error. + */ /**jsdoc * Sets whether or not to bake an asset in the asset server. * @function Assets.setBakingEnabled @@ -155,12 +162,6 @@ public: * @param {boolean} enabled - true to enable baking of the asset, false to disable. * @param {Assets~setBakingEnabledCallback} callback - The function to call upon completion. */ - /**jsdoc - * Called when an {@link Assets.setBakingEnabled} call is complete. - * @callback Assets~setBakingEnabledCallback - * @param {string} error - null if baking was successfully enabled or disabled, otherwise a description of the - * error. - */ // Note: Second callback parameter not documented because it's always {}. Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback); @@ -177,50 +178,32 @@ public: * @typedef {object} Assets.CallbackDetails * @property {object} scope - The scope that the callback function is defined in. This object is bound to * this when the function is called. - * @property {Assets~putAssetCallback|Assets~getAssetCallback|Assets~resolveAssetCallback|Assets~compressDataCallback - * |Assets~decompressDataCallback} + * @property {Assets~compressDataCallback|Assets~decompressDataCallback|Assets~getAssetCallback + * |Assets~getCacheStatusCallback|Assets~loadFromCacheCallback|Assets~putAssetCallback|Assets~queryCacheMetaCallback + * |Assets~resolveAssetCallback|Assets~saveToCacheCallback} * callback - The function to call upon completion. May be an inline function or a function identifier. If a function * identifier, it must be a member of scope. */ - /**jsdoc - * Source and download options for {@link Assets.getAsset}. - * @typedef {object} Assets.GetOptions - * @property {string} url - The mapped path or hash to download. May have a leading "atp:". - * @property {Assets.ResponseType} [responseType="text"] - The desired result type. - * @property {boolean} [decompress=false] - true to gunzip decompress the downloaded data. Synonym: - * compressed. - */ /**jsdoc * Called when an {@link Assets.getAsset} call is complete. * @callback Assets~getAssetCallback * @param {string} error - null if the content was downloaded, otherwise a description of the error. * @param {Assets.GetResult} result - Information on and the content downloaded. */ - /**jsdoc - * Result value returned by {@link Assets.getAsset}. - * @typedef {object} Assets.GetResult - * @property {number} [byteLength] - The number of bytes in the downloaded response. - * @property {boolean} cached - - * @property {string} [contentType] - Automatically detected MIME type of the content. - * @property {boolean} [decompressed] - true if the content was decompressed, false if it wasn't. - * @property {string} [hash] - The hash for the downloaded asset. - * @property {string} [hashURL] - The ATP URL of the hash file. - * @property {string} [path] - The path for the asset, if a path was requested. Otherwise, undefined. - * @property {string|ArrayBuffer|object} [response] - The downloaded content. - * @property {Assets.ResponseType} [responseType] - The type of the downloaded content. - * @property {string} [url] - The URL of the asset requested: the path with leading "atp:" if a path was - * requested, otherwise the requested URL. - * @property {boolean} [wasRedirected] - true if the downloaded data is the baked version of the asset, - * false if it isn't baked. - */ /**jsdoc * Downloads content from the asset server. * @function Assets.getAsset - * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash + * @param {string|Assets.GetOptions} source - What to download and download options. If a string, the mapped path or hash * to download, optionally including a leading "atp:". - * @param {Assets.CallbackDetails|Assets~getAssetCallback} callback - The function to call upon completion. May be a - * function identifier or an inline function. + * @param {object|Assets.CallbackDetails|Assets~getAssetCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~getAssetCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

* @example Retrieve a string from the asset server. * Assets.getAsset( * { @@ -236,30 +219,8 @@ public: * } * ); */ - /**jsdoc - * Downloads content from the asset server. - * @function Assets.getAsset - * @param {Assets.GetOptions|string} source - What to download and download options. If a string, the mapped path or hash - * to download, optionally including a leading "atp:". - * @param {object} scope - The scope that the callback function is defined in. This object is bound to - * this when the function is called. - * @param {Assets~getAssetCallback} callback - The function to call upon completion. May be an inline function, a function - * identifier, or the name of a function in a string. If the name of a function or a function identifier, it must be - * a member of scope. - */ Q_INVOKABLE void getAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); - /**jsdoc - * Content and upload options for {@link Assets.putAsset}. - * @typedef {object} Assets.PutOptions - * @property {string|ArrayBuffer} data - The content to upload. - * @property {string} [path] - A user-friendly path for the file in the asset server. May have a leading - * "atp:". - *

Note: The asset server destroys any unmapped SHA256-named file at server restart. Either set the mapping path - * with this property or use {@link Assets.setMapping} to set a path-to-hash mapping for the uploaded file.

- * @property {boolean} [compress=false] - true to gzip compress the content for upload and storage, - * false to upload and store the data without gzip compression. Synonym: compressed. - */ /**jsdoc * Called when an {@link Assets.putAsset} call is complete. * @callback Assets~putAssetCallback @@ -267,23 +228,19 @@ public: * description of the error. * @param {Assets.PutResult} result - Information on the content uploaded. */ - /**jsdoc - * Result value returned by {@link Assets.putAsset}. - * @typedef {object} Assets.PutResult - * @property {number} [byteLength] - The number of bytes in the hash file stored on the asset server. - * @property {boolean} [compressed] - true if the content stored is gzip compressed. - * @property {string} [contentType] - "application/gzip" if the content stored is gzip compressed. - * @property {string} [hash] - The SHA256 hash of the content. - * @property {string} [url] - The atp: URL of the content: using the path if specified, otherwise the hash. - * @property {string} [path] - The uploaded content's mapped path, if specified. - */ /**jsdoc * Uploads content to the assert server and sets a path-to-hash mapping. * @function Assets.putAsset - * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the + * @param {string|Assets.PutOptions} options - The content to upload and upload options. If a string, the value of the * string is uploaded but a path-to-hash mapping is not set. - * @param {Assets.CallbackDetails|Assets~putAssetCallback} callback - The function to call upon completion. May be an - * inline function or a function identifier. + * @param {object|Assets.CallbackDetails|Assets~putAssetCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~putAssetCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

* @example Store a string in the asset server. * Assets.putAsset( * { @@ -299,17 +256,6 @@ public: * } * ); */ - /**jsdoc - * Uploads content to the assert server and sets a path-to-hash mapping. - * @function Assets.putAsset - * @param {Assets.PutOptions|string} options - The content to upload and upload options. If a string, the value of the - * string is uploaded but a path-to-hash mapping is not set. - * @param {object} scope - The scope that the callback function is defined in. This object is bound to - * this when the function is called. - * @param {Assets~getAssetCallback} callback - The function to call upon completion. May be an inline function, a function - * identifier, or the name of a function in a string. If the name of a function or a function identifier, it must be - * a member of scope. - */ Q_INVOKABLE void putAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc @@ -329,35 +275,25 @@ public: */ Q_INVOKABLE void deleteAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); - /**jsdoc - * Source options for {@link Assets.resolveAsset}. - * @typedef {object} Assets.ResolveOptions - * @property {string} url - The hash or path to resolve. May have a leading "atp:". - */ /**jsdoc * Called when an {@link Assets.resolveAsset} call is complete. * @callback Assets~resolveAssetCallback * @param {string} error - null if the asset hash or path was resolved, otherwise a description of the error. * @param {Assets.ResolveResult} result - Information on the hash or path resolved. */ - /**jsdoc - * Result value returned by {@link Assets.resolveAsset}. - *

Note: If resolving a hash, a file of that hash need not be present on the asset server for the hash to resolve.

- * @typedef {object} Assets.ResolveResult - * @property {string} [hash] - The hash of the asset. - * @property {string} [hashURL] - The url of the asset's hash file, with leading atp:. - * @property {string} [path] - The path to the asset. - * @property {string} [url] - The URL of the asset. - * @property {boolean} [wasRedirected] - true if the resolved data is for the baked version of the asset, - * false if it isn't. - */ /**jsdoc * Resolves and returns information on a hash or a path in the asset server. * @function Assets.resolveAsset * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying * what to resolve. If a string, it may have a leading "atp:". - * @param {Assets.CallbackDetails|Assets~resolveAssetCallback} callback - The function to call upon completion. May be a - * function identifier or an inline function. + * @param {object|Assets.CallbackDetails|Assets~resolveAssetCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~resolveAssetCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

* @example Get the hash and URL for a path. * Assets.resolveAsset( * "/assetsExamples/helloWorld.txt", @@ -371,111 +307,60 @@ public: * } * ); */ - /**jsdoc - * Resolves and returns information on a hash or a path in the asset server. - * @function Assets.resolveAsset - * @param {string|Assets.ResolveOptions} source - The hash or path to resolve if a string, otherwise an object specifying - * what to resolve. If a string, it may have a leading "atp:". - * @param {object} scope - The scope that the callback function is defined in. This object is bound to - * this when the function is called. - * @param {Assets~resolveAssetCallback} callback - The function to call upon completion. May be an inline function, a - * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it - * must be a member of scope. - */ Q_INVOKABLE void resolveAsset(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); - /**jsdoc - * Content and decompression options for {@link Assets.decompressData}. - * @typedef {object} Assets.DecompressOptions - * @property {ArrayBuffer} data - The data to decompress. - * @property {Assets.ResponseType} [responseType=text] - The type of data to return. - */ /**jsdoc * Called when an {@link Assets.decompressData} call is complete. * @callback Assets~decompressDataCallback * @param {string} error - null if the data was successfully compressed, otherwise a description of the error. * @param {Assets.DecompressResult} result - Information on and the decompressed data. */ - /**jsdoc - * Result value returned by {@link Assets.decompressData}. - * @typedef {object} Assets.DecompressResult - * @property {number} [byteLength] - The number of bytes in the decompressed data. - * @property {string} [contentType] - The MIME type of the decompressed data. - * @property {boolean} [decompressed] - true if the data is decompressed. - * @property {string|ArrayBuffer|object} [response] - The decompressed data. - * @property {Assets.ResponseType} [responseType] - The type of the response. - */ /**jsdoc * Decompresses data in memory using gunzip. * @function Assets.decompressData * @param {Assets.DecompressOptions} source - What to decompress and decompression options. - * @param {Assets.CallbackDetails|Assets~decompressDataCallback} callback - The function to call upon completion. May be a - * function identifier or an inline function. - */ - /**jsdoc - * Decompresses data in memory using gunzip. - * @function Assets.decompressData - * @param {Assets.DecompressOptions} source - What to decompress and decompression options. - * @param {object} scope - The scope that the callback function is defined in. This object is bound to - * this when the function is called. - * @param {Assets~decompressDataCallback} callback - The function to call upon completion. May be an inline function, a - * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it - * must be a member of scope. + * @param {object|Assets.CallbackDetails|Assets~decompressDataCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~decompressDataCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

*/ Q_INVOKABLE void decompressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); - /**jsdoc - * Content and compression options for {@link Assets.compressData}. - * @typedef {object} Assets.CompressOptions - * @property {ArrayBuffer|string} data - The data to compress. - * @property {number} level - The compression level, range -19. -1 means - * use the default gzip compression level, 0 means no compression, and 9 means maximum - * compression. - */ /**jsdoc * Called when an {@link Assets.compressData} call is complete. * @callback Assets~compressDataCallback * @param {string} error - null if the data was successfully compressed, otherwise a description of the error. * @param {Assets.CompressResult} result - Information on and the compressed data. */ - /**jsdoc - * Result value returned by {@link Assets.compressData}. - * @typedef {object} Assets.CompressResult - * @property {boolean} [compressed] - true if the data is compressed. - * @property {number} [byteLength] - The number of bytes in the compressed data. - * @property {string} [contentType] - The MIME type of the compressed data, i.e., "application/gzip". - * @property {ArrayBuffer} [data] - The compressed data. - */ /**jsdoc * Compresses data in memory using gzip. * @function Assets.compressData - * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compression options. If an ArrayBuffer - * or a string, the data to compress. - * @param {Assets.CallbackDetails|Assets~compressDataCallback} callback - The function to call upon completion. May be a - * function identifier or an inline function. - */ - /**jsdoc - * Compresses data in memory using gzip. - * @function Assets.compressData - * @param {Assets.CompressOptions|ArrayBuffer|string} source - What to compress and compression options. If an ArrayBuffer - * or a string, the data to compress. - * @param {object} scope - The scope that the callback function is defined in. This object is bound to - * this when the function is called. - * @param {Assets~compressDataCallback} callback - The function to call upon completion. May be an inline function, a - * function identifier, or the name of a function in a string. If the name of a function or a function identifier, it - * must be a member of scope. + * @param {string|ArrayBuffer|Assets.CompressOptions} source - What to compress and compression options. If a string or + * ArrayBuffer, the data to compress. + * @param {object|Assets.CallbackDetails|Assets~compressDataCallback} scopeOrCallback - If an object, then the scope that + * the callback function is defined in. This object is bound to this when the function is + * called. + *

Otherwise, the function to call upon completion. This may be an inline function or a function identifier.

+ * @param {Assets~compressDataCallback} [callback] - Used if scopeOrCallback specifies the scope. + *

The function to call upon completion. May be an inline function, a function identifier, or the name of a function + * in a string. If the name of a function or a function identifier, it must be a member of the scope specified by + * scopeOrCallback.

*/ Q_INVOKABLE void compressData(QScriptValue options, QScriptValue scope, QScriptValue callback = QScriptValue()); /**jsdoc - * Initializes the local cache if it isn't already initialized. + * Initializes the cache if it isn't already initialized. * @function Assets.initializeCache - * @returns {boolean} true if the local cache is initialized, false if it isn't. + * @returns {boolean} true if the cache is initialized, false if it isn't. */ Q_INVOKABLE bool initializeCache() { return Parent::initializeCache(); } /**jsdoc - * Checks whether the script can write to the local cache. + * Checks whether the script can write to the cache. * @function Assets.canWriteCacheValue * @param {string} url - Not used. * @returns {boolean} true if the script is an Interface, avatar, or assignment client script, @@ -490,7 +375,7 @@ public: * @callback Assets~getCacheStatusCallback * @param {string} error - null if the cache status was retrieved without error, otherwise a description of * the error. - * @param {Assets.GetCacheStatusResult} status - Details of the current cache status. + * @param {Assets.GetCacheStatusResult} result - Details of the current cache status. */ /**jsdoc * Gets the current cache status. @@ -518,13 +403,14 @@ public: * Called when {@link Assets.queryCacheMeta} is complete. * @callback Assets~queryCacheMetaCallback * @param {string} error - null if the URL has a valid cache entry, otherwise a description of the error. - * @param {Assets.CacheItemMetaData} queryResult - Information on an asset in the cache. + * @param {Assets.CacheItemMetaData} result - Information on an asset in the cache. */ /**jsdoc * Gets information about the status of an asset in the cache. * @function Assets.queryCacheMeta - * @param {string|object} path - The URL of the cached asset to get information on. If an object then path.url - * is used. Must start with "atp:" or "cache:". + * @param {string|Assets.QueryCacheMetaOptions} path - The URL of the cached asset to get information on if a string, + * otherwise an object specifying the cached asset to get information on. The URL must start with "atp:" + * or "cache:". * @param {object|Assets.CallbackDetails|Assets~queryCacheMetaCallback} scopeOrCallback - If an object, then the scope that * the callback function is defined in. This object is bound to this when the function is * called. From 06ebba554e0e6fee5f6ba84be6756c36f42116e1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 5 Sep 2019 12:23:12 +1200 Subject: [PATCH 08/32] Doc review --- libraries/script-engine/src/AssetScriptingInterface.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index c4c7940a80..325e177964 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -26,12 +26,11 @@ /**jsdoc * The Assets API provides facilities for interacting with the domain's asset server and the client cache. - * cache. *

Assets are stored in the asset server in files with SHA256 names. These files are mapped to user-friendly URLs of the * format: atp:/path/filename. The assets may optionally be baked, in which case a request for the original * unbaked version of the asset is automatically redirected to the baked version. The asset data may optionally be stored as * compressed.

- *

The client cache can be access directly, using "atp:" "cache:" URLs. Interface, avatar, and + *

The client cache can be access directly, using "atp:" or "cache:" URLs. Interface, avatar, and * assignment client scripts can write to the cache. All script types can read from the cache.

* * @namespace Assets @@ -82,7 +81,7 @@ public: * @param {Assets.DownloadDataError} error - The success or failure of the download. */ /**jsdoc - * Downloads content from the asset server, form a SHA256-named file. + * Downloads content from the asset server, from a SHA256-named file. * @function Assets.downloadData * @param {string} url - The raw URL of asset to download: atp: followed by the assets's SHA256 hash. * @param {Assets~downloadDataCallback} callback - The function to call upon completion. @@ -93,7 +92,7 @@ public: * Assets.uploadData("Hello world!", function (url, hash) { * assetURL = url; * print("url: " + assetURL); // atp:a0g89... - * Assets.setMapping("/assetsExamples/hellowWorld.txt", hash, function (error) { + * Assets.setMapping("/assetsExamples/helloWorld.txt", hash, function (error) { * if (error) { * print("ERROR: Could not set mapping!"); * return; @@ -229,7 +228,7 @@ public: * @param {Assets.PutResult} result - Information on the content uploaded. */ /**jsdoc - * Uploads content to the assert server and sets a path-to-hash mapping. + * Uploads content to the asset server and sets a path-to-hash mapping. * @function Assets.putAsset * @param {string|Assets.PutOptions} options - The content to upload and upload options. If a string, the value of the * string is uploaded but a path-to-hash mapping is not set. From 49ab5042268569215c7431d5ded45a91ece263c1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 5 Sep 2019 12:23:56 +1200 Subject: [PATCH 09/32] Make placement of callback JSDoc consistent --- .../script-engine/src/AssetScriptingInterface.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 325e177964..5da3c51a08 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -47,6 +47,13 @@ public: using Parent = BaseAssetScriptingInterface; AssetScriptingInterface(QObject* parent = nullptr); + /**jsdoc + * Called when an {@link Assets.uploadData} call is complete. + * @callback Assets~uploadDataCallback + * @param {string} url - The raw URL of the file that the content is stored in, with atp: as the scheme and + * the SHA256 hash as the filename (with no extension). + * @param {string} hash - The SHA256 hash of the content. + */ /**jsdoc * Uploads content to the asset server, storing it in a SHA256-named file. *

Note: The asset server destroys any unmapped SHA256-named file at server restart. Use {@link Assets.setMapping} to @@ -65,13 +72,6 @@ public: * }); * }); */ - /**jsdoc - * Called when an {@link Assets.uploadData} call is complete. - * @callback Assets~uploadDataCallback - * @param {string} url - The raw URL of the file that the content is stored in, with atp: as the scheme and - * the SHA256 hash as the filename (with no extension). - * @param {string} hash - The SHA256 hash of the content. - */ Q_INVOKABLE void uploadData(QString data, QScriptValue callback); /**jsdoc From 5aef2d674a4d02cccf5c495926d3dbc86f392155 Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 10:12:59 -0700 Subject: [PATCH 10/32] DOC-69: Move Supported Script Types section to top of JSDoc pages --- tools/jsdoc/plugins/hifi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 67dafe5a16..07549530ce 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -125,7 +125,7 @@ exports.handlers = { if (rows.length > 0) { var availableIn = "

Supported Script Types: " + rows.join(" • ") + "

"; - e.doclet.description = (e.doclet.description ? e.doclet.description : "") + availableIn; + e.doclet.description = availableIn + (e.doclet.description ? e.doclet.description : ""); } } From 5ebf141d7374e77dbfb5cb297ecfa7da111ee127 Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 10:39:18 -0700 Subject: [PATCH 11/32] DOC-67: Incorrect location of example code for Avatar API --- .../hifi-jsdoc-template/tmpl/container.tmpl | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl index 2a5e863d6d..7dabba0809 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl @@ -92,35 +92,29 @@ - - - - -

Example 1? 's':'' ?>

- - +

Description

- -

Classes

- -

- - - -

Example 1? 's':'' ?>

+ +

Classes

+ +

+ + + + @@ -245,7 +239,7 @@ - + Date: Thu, 5 Sep 2019 11:08:25 -0700 Subject: [PATCH 12/32] DOC-156: Fix font size issues on non-paragraph text --- tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css index a33cff15e4..fabdfa35d0 100644 --- a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css +++ b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css @@ -38,6 +38,7 @@ body font-weight: 400; color: #000000; letter-spacing: 0.5px; + font-size: 0.95rem; } p { From af7e68786a5ad8583fcdba81e01e7faa4968fcee Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 11:29:13 -0700 Subject: [PATCH 13/32] DOC-156 (comment): Fixed spacing on non-paragraph text --- tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css index fabdfa35d0..391f1c6da4 100644 --- a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css +++ b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css @@ -23,7 +23,7 @@ ********************************************************************/ * { - box-sizing: border-box + box-sizing: border-box; } html @@ -39,6 +39,7 @@ body color: #000000; letter-spacing: 0.5px; font-size: 0.95rem; + line-height: 20px; } p { @@ -423,7 +424,6 @@ header { display: block; text-align: center; font-size: 90%; - margin-top: -20px; } .variation { From d8c655a151d1fb07a54a776f6205cd320838fe8c Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 11:42:34 -0700 Subject: [PATCH 14/32] Fixed spacing issues in navigation caused by fix to to DOC-156 --- tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css index 391f1c6da4..c8deaf4b6d 100644 --- a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css +++ b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css @@ -44,7 +44,6 @@ body p { font-size: 0.95rem; - line-height: 20px; } section @@ -201,6 +200,7 @@ nav { padding-left: 20px; padding-right: 20px; box-sizing: border-box; + line-height: 12px; } nav::-webkit-scrollbar { From b8b7dbf6819f5e4a2d9c17abc46b94c08242743d Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 13:39:06 -0700 Subject: [PATCH 15/32] DOC-148: Increase size of monospace type --- tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css index c8deaf4b6d..2386f88586 100644 --- a/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css +++ b/tools/jsdoc/hifi-jsdoc-template/static/styles/jsdoc.css @@ -129,7 +129,6 @@ table { thead { border-color: #d8e1d9; background:#d8e1d9; - text-align: left; } table tr { @@ -147,6 +146,7 @@ td { article table thead tr th, article table tbody tr td, article table tbody tr td p { font-size: .89rem; line-height: 20px; + text-align: left; } article table thead tr th, article table tbody tr td { @@ -380,12 +380,12 @@ nav > h2 > a { tt, code, kbd, samp { font-family: Consolas, Monaco, 'Andale Mono', monospace; - font-size: 0.9rem; + font-size: 1.05em; } .name, .signature { font-family: Consolas, Monaco, 'Andale Mono', monospace; - font-size: 0.9rem; + font-size: 1.05em; } img { @@ -538,7 +538,7 @@ header { .prettyprint code { - font-size: 0.7rem; + font-size: 0.9em; line-height: 18px; display: block; padding: 4px 12px; From 55bdc7d2a61db8f9fdc24beb6b2b4c46cdd2ce3a Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 13:54:55 -0700 Subject: [PATCH 16/32] Made sure ctrlaltdavid's changes made it into my repo --- tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl index 7dabba0809..f9c6c42924 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl @@ -94,6 +94,10 @@ + +

Example 1? 's':'' ?>

+ +

Description

@@ -184,7 +188,7 @@

Members

- + From 2322a9ea7f42e264f54af732685eed8b784a7f26 Mon Sep 17 00:00:00 2001 From: ingerjm0 Date: Thu, 5 Sep 2019 13:58:59 -0700 Subject: [PATCH 17/32] Making sure ctrlaltdavid's changes made it into my repository --- tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl index f9c6c42924..acbab36d8d 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl @@ -188,7 +188,7 @@

Members

- + @@ -243,7 +243,7 @@ - + Date: Mon, 9 Sep 2019 17:32:27 -0700 Subject: [PATCH 18/32] Give better error message when draco part of model baking fails --- libraries/baking/src/ModelBaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 9d6a368e1c..46da29cf00 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -437,7 +437,7 @@ void ModelBaker::abort() { bool ModelBaker::buildDracoMeshNode(FBXNode& dracoMeshNode, const QByteArray& dracoMeshBytes, const std::vector& dracoMaterialList) { if (dracoMeshBytes.isEmpty()) { - handleError("Failed to finalize the baking of a draco Geometry node"); + handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString()); return false; } From 384b7fd6292927cba8f4f4fb712335d8a1aaebf6 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 10 Sep 2019 12:21:36 -0700 Subject: [PATCH 19/32] Fix FBXBaker failing when an unbaked mesh is empty Error early for all model bakers for draco errors --- libraries/baking/src/ModelBaker.cpp | 10 ++++++++-- libraries/baking/src/OBJBaker.h | 2 +- libraries/hfm/src/hfm/HFM.h | 2 +- libraries/model-baker/src/model-baker/Baker.cpp | 13 +++++++++---- libraries/model-baker/src/model-baker/Baker.h | 1 + .../src/model-baker/BuildDracoMeshTask.cpp | 17 +++++++++++------ .../src/model-baker/BuildDracoMeshTask.h | 2 +- 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 46da29cf00..adf6deeb56 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -246,6 +246,13 @@ void ModelBaker::bakeSourceCopy() { // Begin hfm baking baker.run(); + for (auto error : baker.getDracoErrors()) { + if (error) { + handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString()); + return; + } + } + _hfmModel = baker.getHFMModel(); _materialMapping = baker.getMaterialMapping(); dracoMeshes = baker.getDracoMeshes(); @@ -437,8 +444,7 @@ void ModelBaker::abort() { bool ModelBaker::buildDracoMeshNode(FBXNode& dracoMeshNode, const QByteArray& dracoMeshBytes, const std::vector& dracoMaterialList) { if (dracoMeshBytes.isEmpty()) { - handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString()); - return false; + handleWarning("Empty mesh detected in model: '" + _modelURL.toString() + "'. It will be included in the baked output."); } FBXNode dracoNode; diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 9d0fe53e3c..55adec5786 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -28,7 +28,7 @@ protected: private: void createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh); - void setMaterialNodeProperties(FBXNode& materialNode, QString material, const hfm::Model::Pointer& hfmModel); + void setMaterialNodeProperties(FBXNode& materialNode, QString material, const hfm::Model::Pointer& hfmModel); NodeID nextNodeID() { return _nodeID++; } NodeID _nodeID { 0 }; diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 484a10aa3b..888e562bca 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -56,7 +56,7 @@ const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; using ShapeVertices = std::vector; // The version of the Draco mesh binary data itself. See also: FBX_DRACO_MESH_VERSION in FBX.h -static const int DRACO_MESH_VERSION = 2; +static const int DRACO_MESH_VERSION = 3; static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000; static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES; diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index c896613df5..47a8db82b8 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -120,7 +120,7 @@ namespace baker { class BakerEngineBuilder { public: using Input = VaryingSet3; - using Output = VaryingSet4, std::vector>>; + using Output = VaryingSet5, std::vector, std::vector>>; using JobModel = Task::ModelIO; void build(JobModel& model, const Varying& input, Varying& output) { const auto& hfmModelIn = input.getN(0); @@ -168,7 +168,8 @@ namespace baker { const auto buildDracoMeshInputs = BuildDracoMeshTask::Input(meshesIn, normalsPerMesh, tangentsPerMesh).asVarying(); const auto buildDracoMeshOutputs = model.addJob("BuildDracoMesh", buildDracoMeshInputs); const auto dracoMeshes = buildDracoMeshOutputs.getN(0); - const auto materialList = buildDracoMeshOutputs.getN(1); + const auto dracoErrors = buildDracoMeshOutputs.getN(1); + const auto materialList = buildDracoMeshOutputs.getN(2); // Parse flow data const auto flowData = model.addJob("ParseFlowData", mapping); @@ -181,7 +182,7 @@ namespace baker { const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices, flowData).asVarying(); const auto hfmModelOut = model.addJob("BuildModel", buildModelInputs); - output = Output(hfmModelOut, materialMapping, dracoMeshes, materialList); + output = Output(hfmModelOut, materialMapping, dracoMeshes, dracoErrors, materialList); } }; @@ -212,7 +213,11 @@ namespace baker { return _engine->getOutput().get().get2(); } - std::vector> Baker::getDracoMaterialLists() const { + std::vector Baker::getDracoErrors() const { return _engine->getOutput().get().get3(); } + + std::vector> Baker::getDracoMaterialLists() const { + return _engine->getOutput().get().get4(); + } }; diff --git a/libraries/model-baker/src/model-baker/Baker.h b/libraries/model-baker/src/model-baker/Baker.h index 6f74cb646e..9780484fa4 100644 --- a/libraries/model-baker/src/model-baker/Baker.h +++ b/libraries/model-baker/src/model-baker/Baker.h @@ -33,6 +33,7 @@ namespace baker { hfm::Model::Pointer getHFMModel() const; MaterialMapping getMaterialMapping() const; const std::vector& getDracoMeshes() const; + std::vector getDracoErrors() const; // This is a ByteArray and not a std::string because the character sequence can contain the null character (particularly for FBX materials) std::vector> getDracoMaterialLists() const; diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp index 25a45cefe5..180664a52a 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp @@ -51,7 +51,7 @@ std::vector createMaterialList(const hfm::Mesh& mesh) { return materialList; } -std::unique_ptr createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& materialList) { +std::tuple, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& materialList) { Q_ASSERT(normals.size() == 0 || (int)normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); @@ -68,7 +68,7 @@ std::unique_ptr createDracoMesh(const hfm::Mesh& mesh, const std::v } if (numTriangles == 0) { - return std::unique_ptr(); + return std::make_tuple(std::unique_ptr(), false); } draco::TriangleSoupMeshBuilder meshBuilder; @@ -184,7 +184,7 @@ std::unique_ptr createDracoMesh(const hfm::Mesh& mesh, const std::v if (!dracoMesh) { qCWarning(model_baker) << "Failed to finalize the baking of a draco Geometry node"; - return std::unique_ptr(); + return std::make_tuple(std::unique_ptr(), true); } // we need to modify unique attribute IDs for custom attributes @@ -201,7 +201,7 @@ std::unique_ptr createDracoMesh(const hfm::Mesh& mesh, const std::v dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX); } - return dracoMesh; + return std::make_tuple(std::move(dracoMesh), false); } #endif // not Q_OS_ANDROID @@ -218,20 +218,25 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp const auto& normalsPerMesh = input.get1(); const auto& tangentsPerMesh = input.get2(); auto& dracoBytesPerMesh = output.edit0(); - auto& materialLists = output.edit1(); + auto& dracoErrorsPerMesh = output.edit1(); + auto& materialLists = output.edit2(); dracoBytesPerMesh.reserve(meshes.size()); + dracoErrorsPerMesh.reserve(meshes.size()); materialLists.reserve(meshes.size()); for (size_t i = 0; i < meshes.size(); i++) { const auto& mesh = meshes[i]; const auto& normals = baker::safeGet(normalsPerMesh, i); const auto& tangents = baker::safeGet(tangentsPerMesh, i); dracoBytesPerMesh.emplace_back(); + dracoErrorsPerMesh.emplace_back(); auto& dracoBytes = dracoBytesPerMesh.back(); + auto& dracoError = dracoErrorsPerMesh.back(); materialLists.push_back(createMaterialList(mesh)); const auto& materialList = materialLists.back(); - auto dracoMesh = createDracoMesh(mesh, normals, tangents, materialList); + std::unique_ptr dracoMesh; + std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList); if (dracoMesh) { draco::Encoder encoder; diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h index 0e33be3c41..ac9ad648ab 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h @@ -34,7 +34,7 @@ class BuildDracoMeshTask { public: using Config = BuildDracoMeshConfig; using Input = baker::VaryingSet3, baker::NormalsPerMesh, baker::TangentsPerMesh>; - using Output = baker::VaryingSet2, std::vector>>; + using Output = baker::VaryingSet3, std::vector, std::vector>>; using JobModel = baker::Job::ModelIO; void configure(const Config& config); From 65d85138ccb1477ebadc3f17b89b8b1abddd2668 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 10 Sep 2019 16:31:30 -0700 Subject: [PATCH 20/32] TIL that std::vector is a bit field https://howardhinnant.github.io/onvectorbool.html --- libraries/baking/src/ModelBaker.cpp | 9 ++++----- .../model-baker/src/model-baker/BuildDracoMeshTask.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index adf6deeb56..70290fe283 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -246,11 +246,10 @@ void ModelBaker::bakeSourceCopy() { // Begin hfm baking baker.run(); - for (auto error : baker.getDracoErrors()) { - if (error) { - handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString()); - return; - } + const auto& errors = baker.getDracoErrors(); + if (std::find(errors.cbegin(), errors.cend(), true) != errors.cend()) { + handleError("Failed to finalize the baking of a draco Geometry node from model " + _modelURL.toString()); + return; } _hfmModel = baker.getHFMModel(); diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp index 180664a52a..9fff570cc0 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp @@ -222,21 +222,23 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp auto& materialLists = output.edit2(); dracoBytesPerMesh.reserve(meshes.size()); - dracoErrorsPerMesh.reserve(meshes.size()); + // vector is an exception to the std::vector conventions as it is a bit field + // So a bool reference to an element doesn't work + dracoErrorsPerMesh.resize(meshes.size()); materialLists.reserve(meshes.size()); for (size_t i = 0; i < meshes.size(); i++) { const auto& mesh = meshes[i]; const auto& normals = baker::safeGet(normalsPerMesh, i); const auto& tangents = baker::safeGet(tangentsPerMesh, i); dracoBytesPerMesh.emplace_back(); - dracoErrorsPerMesh.emplace_back(); auto& dracoBytes = dracoBytesPerMesh.back(); - auto& dracoError = dracoErrorsPerMesh.back(); materialLists.push_back(createMaterialList(mesh)); const auto& materialList = materialLists.back(); + bool dracoError; std::unique_ptr dracoMesh; std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList); + dracoErrorsPerMesh[dracoErrorsPerMesh.size()-1] = dracoError; if (dracoMesh) { draco::Encoder encoder; From b969a9b1e02689ea692d587529462a9f0f6ee972 Mon Sep 17 00:00:00 2001 From: dante ruiz Date: Tue, 10 Sep 2019 16:50:50 -0700 Subject: [PATCH 21/32] fix tablet html loading errors --- .../ui/src/ui/TabletScriptingInterface.cpp | 60 ++++++++++++++++++- .../ui/src/ui/TabletScriptingInterface.h | 4 ++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index c54f63690d..6c57314367 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,59 @@ 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; + /*QObject* root = nullptr; + if (!_toolbarMode && _qmlTabletRoot) { + root = _qmlTabletRoot; + } else if (_toolbarMode && _desktopWindow) { + root = _desktopWindow->asQuickItem(); + } + + if (root) { + // BUGZ-1398: tablet access to local HTML files from client scripts + // Here we TEMPORARILY mark the main thread as allowed to load local file content, + // because the thread that originally made the call is so marked. + if (localSafeContext) { + hifi::scripting::setLocalAccessSafeThread(true); + } + QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); + hifi::scripting::setLocalAccessSafeThread(false); + _state = State::QML; + _currentPathLoaded = path; + QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); + if (_toolbarMode && _desktopWindow) { + QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(resizable))); + } + + } else { + qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; + }*/ } 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 +883,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 +895,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 From 639beee6cbdb7b4222aab08b182c62be36eab24f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 11 Sep 2019 15:21:49 -0700 Subject: [PATCH 22/32] Fix logic for reliable service address-change when new add already used --- libraries/networking/src/udt/Socket.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 4c01517346..67af69ae8f 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -546,7 +546,6 @@ void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) { void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAddr currentAddress) { { Lock connectionsLock(_connectionsHashMutex); - _connectionsHash.erase(currentAddress); const auto connectionIter = _connectionsHash.find(previousAddress); if (connectionIter != _connectionsHash.end()) { @@ -554,18 +553,16 @@ void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAdd _connectionsHash.erase(connectionIter); connection->setDestinationAddress(currentAddress); _connectionsHash[currentAddress] = move(connection); - } - } + connectionsLock.release(); - { - Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex); - _unreliableSequenceNumbers.erase(currentAddress); + Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex); + const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress); + if (sequenceNumbersIter != _unreliableSequenceNumbers.end()) { + auto sequenceNumbers = sequenceNumbersIter->second; + _unreliableSequenceNumbers.erase(sequenceNumbersIter); + _unreliableSequenceNumbers[currentAddress] = sequenceNumbers; + } - const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress); - if (sequenceNumbersIter != _unreliableSequenceNumbers.end()) { - auto sequenceNumbers = sequenceNumbersIter->second; - _unreliableSequenceNumbers.erase(sequenceNumbersIter); - _unreliableSequenceNumbers[currentAddress] = sequenceNumbers; } } } From fad1296180e6b869177341663af5c806e92cc037 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 12 Sep 2019 11:02:10 +1200 Subject: [PATCH 23/32] Fix up Workload JSDoc stubs --- libraries/task/src/task/Config.h | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/libraries/task/src/task/Config.h b/libraries/task/src/task/Config.h index 8accba9e1f..71d48c9a18 100644 --- a/libraries/task/src/task/Config.h +++ b/libraries/task/src/task/Config.h @@ -90,6 +90,17 @@ public: using Config = JobConfig; }; +/**jsdoc + * @namespace Workload + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {number} cpuRunTime - Read-only. + * @property {boolean} enabled + * @property {number} branch + */ // A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled) class JobConfig : public QObject { Q_OBJECT @@ -139,7 +150,7 @@ public: double getCPURunTime() const { return _msCPURunTime; } /**jsdoc - * @function Render.getConfig + * @function Workload.getConfig * @param {string} name * @returns {object} */ @@ -162,19 +173,19 @@ public: // Describe the node graph data connections of the associated Job/Task /**jsdoc - * @function JobConfig.isTask + * @function Workload.isTask * @returns {boolean} */ Q_INVOKABLE bool isTask() const { return _isTask; } /**jsdoc - * @function JobConfig.isSwitch + * @function Workload.isSwitch * @returns {boolean} */ Q_INVOKABLE bool isSwitch() const { return _isSwitch; } /**jsdoc - * @function JobConfig.getSubConfigs + * @function Workload.getSubConfigs * @returns {object[]} */ Q_INVOKABLE QObjectList getSubConfigs() const { @@ -187,13 +198,13 @@ public: } /**jsdoc - * @function JobConfig.getNumSubs + * @function Workload.getNumSubs * @returns {number} */ Q_INVOKABLE int getNumSubs() const { return getSubConfigs().size(); } /**jsdoc - * @function JobConfig.getSubConfig + * @function Workload.getSubConfig * @param {number} index * @returns {object} */ @@ -214,7 +225,7 @@ public slots: /**jsdoc * @function Workload.load - * @param {object} map + * @param {object} json */ void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } From d548afb2771d0f83117cd96ae92454cc58a3efa2 Mon Sep 17 00:00:00 2001 From: jennaingersoll <42979611+jennaingersoll@users.noreply.github.com> Date: Wed, 11 Sep 2019 17:06:35 -0700 Subject: [PATCH 24/32] Removed indent per review --- tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl index acbab36d8d..5c149fa434 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/container.tmpl @@ -92,7 +92,7 @@ - +

Example 1? 's':'' ?>

From 12b980d99dafc779243c3c646247b5c437cba7a6 Mon Sep 17 00:00:00 2001 From: milad Date: Wed, 11 Sep 2019 17:08:35 -0700 Subject: [PATCH 25/32] about to test if this new right click disable is working --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fd82567a94..0279963259 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -800,7 +800,7 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { - if (!EntityTree::areEntityClicksCaptured()) { + if (!EntityTree::areEntityClicksCaptured() && event->button() == Qt::MouseButton::LeftButton) { auto properties = entity->getProperties(); QString urlString = properties.getHref(); QUrl url = QUrl(urlString, QUrl::StrictMode); From f7f0483320669d907cf84d618517e97901733416 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 12 Sep 2019 08:41:35 -0700 Subject: [PATCH 26/32] Don't disable HMD leaning while seated --- interface/src/avatar/MyAvatar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4d1c20010c..ae84347d17 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6257,7 +6257,6 @@ void MyAvatar::beginSit(const glm::vec3& position, const glm::quat& rotation) { _characterController.setSeated(true); setCollisionsEnabled(false); - setHMDLeanRecenterEnabled(false); // Disable movement setSitDriveKeysStatus(false); centerBody(); @@ -6276,7 +6275,6 @@ void MyAvatar::endSit(const glm::vec3& position, const glm::quat& rotation) { clearPinOnJoint(getJointIndex("Hips")); _characterController.setSeated(false); setCollisionsEnabled(true); - setHMDLeanRecenterEnabled(true); centerBody(); slamPosition(position); setWorldOrientation(rotation); From 00f7b75f34f55f3ffddcc67ceff103a9102039ce Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 12 Sep 2019 10:45:35 -0700 Subject: [PATCH 27/32] Fix draco errors being added to wrong part of list --- libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp index 9fff570cc0..12347c30b1 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp @@ -238,7 +238,7 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp bool dracoError; std::unique_ptr dracoMesh; std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList); - dracoErrorsPerMesh[dracoErrorsPerMesh.size()-1] = dracoError; + dracoErrorsPerMesh[i] = dracoError; if (dracoMesh) { draco::Encoder encoder; From 2d68cfa5e7f6a3306d7a7ad64bef2d429fa53dcb Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 12 Sep 2019 12:04:24 -0700 Subject: [PATCH 28/32] Use unique_lock::unlock() instead of unique_lock::release() ... which actually has the opposite effect --- libraries/networking/src/udt/Socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 67af69ae8f..20cb30dbd8 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -553,7 +553,7 @@ void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAdd _connectionsHash.erase(connectionIter); connection->setDestinationAddress(currentAddress); _connectionsHash[currentAddress] = move(connection); - connectionsLock.release(); + connectionsLock.unlock(); Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex); const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress); From 3c043309be8ea4dd160e61d038476c949baea2a0 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 12 Sep 2019 14:03:17 -0700 Subject: [PATCH 29/32] Detect HMD mode properly from rig, in order to enable head IK --- libraries/animation/src/Rig.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 561995cce4..5fe2cb33ff 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1958,8 +1958,7 @@ void Rig::updateReactions(const ControllerParameters& params) { bool isSeated = _state == RigRole::Seated; bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled; - bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated; - bool hmdMode = hipsEnabled && !hipsEstimated; + bool hmdMode = hipsEnabled; if ((reactionPlaying || isSeated) && !hmdMode) { // TODO: make this smooth. From 4aa85baf75c8894ddb3e25d525392627c1c00bca Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 12 Sep 2019 14:07:56 -0700 Subject: [PATCH 30/32] Restate disable leaning when seated --- interface/src/avatar/MyAvatar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ae84347d17..4d1c20010c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -6257,6 +6257,7 @@ void MyAvatar::beginSit(const glm::vec3& position, const glm::quat& rotation) { _characterController.setSeated(true); setCollisionsEnabled(false); + setHMDLeanRecenterEnabled(false); // Disable movement setSitDriveKeysStatus(false); centerBody(); @@ -6275,6 +6276,7 @@ void MyAvatar::endSit(const glm::vec3& position, const glm::quat& rotation) { clearPinOnJoint(getJointIndex("Hips")); _characterController.setSeated(false); setCollisionsEnabled(true); + setHMDLeanRecenterEnabled(true); centerBody(); slamPosition(position); setWorldOrientation(rotation); From bc119d6c850e62d2fc10f56d2c3440a6772fce5e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 12 Sep 2019 17:02:19 -0700 Subject: [PATCH 31/32] don't expect final kinematic simulation update --- libraries/physics/src/EntityMotionState.cpp | 73 +++++++++++---------- 1 file changed, 38 insertions(+), 35 deletions(-) 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) { From 8491a3792385a636298c527669a5ecbaec5ed4b6 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Thu, 12 Sep 2019 17:12:50 -0700 Subject: [PATCH 32/32] fixing WebEntities html --- interface/resources/qml/Web3DSurface.qml | 33 ++++++++++++++----- .../src/RenderableWebEntityItem.cpp | 10 ++++++ libraries/entities/src/WebEntityItem.cpp | 10 ++++++ libraries/entities/src/WebEntityItem.h | 3 ++ .../ui/src/ui/TabletScriptingInterface.cpp | 26 --------------- 5 files changed, 47 insertions(+), 35 deletions(-) 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/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6c57314367..3465138e00 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -834,32 +834,6 @@ void TabletProxy::loadHTMLSourceImpl(const QVariant& url, const QString& injectJ hifi::scripting::setLocalAccessSafeThread(false); } _state = State::Web; - /*QObject* root = nullptr; - if (!_toolbarMode && _qmlTabletRoot) { - root = _qmlTabletRoot; - } else if (_toolbarMode && _desktopWindow) { - root = _desktopWindow->asQuickItem(); - } - - if (root) { - // BUGZ-1398: tablet access to local HTML files from client scripts - // Here we TEMPORARILY mark the main thread as allowed to load local file content, - // because the thread that originally made the call is so marked. - if (localSafeContext) { - hifi::scripting::setLocalAccessSafeThread(true); - } - QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); - hifi::scripting::setLocalAccessSafeThread(false); - _state = State::QML; - _currentPathLoaded = path; - QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); - if (_toolbarMode && _desktopWindow) { - QMetaObject::invokeMethod(root, "setResizable", Q_ARG(const QVariant&, QVariant(resizable))); - } - - } else { - qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; - }*/ } void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase) {