From 025c136561a8b692f19babe00b675a66045999ef Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 17 Dec 2019 09:19:47 +1300 Subject: [PATCH 1/2] XMLHttpRequest API JSDoc --- .../script-engine/src/XMLHttpRequestClass.cpp | 8 + .../script-engine/src/XMLHttpRequestClass.h | 247 ++++++++++++++++++ 2 files changed, 255 insertions(+) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 571c0e2a0e..57514a2231 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -110,6 +110,10 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { return QScriptValue::NullValue; } +/**jsdoc + * Called when the request's ready state changes. + * @callback XMLHttpRequest~onReadyStateChangeCallback + */ void XMLHttpRequestClass::setReadyState(ReadyState readyState) { if (readyState != _readyState) { _readyState = readyState; @@ -183,6 +187,10 @@ void XMLHttpRequestClass::doSend() { } } +/**jsdoc + * Called when the request times out. + * @callback XMLHttpRequest~onTimeoutCallback + */ void XMLHttpRequestClass::requestTimeout() { if (_onTimeout.isFunction()) { _onTimeout.call(QScriptValue::NullValue); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index d7f3c2e059..cb86cafb67 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -21,6 +21,132 @@ #include <QScriptValue> #include <QTimer> +/* +XMlHttpRequest object +XMlHttpRequest.objectName string +XMlHttpRequest.response undefined +XMlHttpRequest.responseText string +XMlHttpRequest.responseType string +XMlHttpRequest.status number +XMlHttpRequest.statusText string +XMlHttpRequest.readyState number +XMlHttpRequest.errorCode number +XMlHttpRequest.timeout number +XMlHttpRequest.UNSENT number +XMlHttpRequest.OPENED number +XMlHttpRequest.HEADERS_RECEIVED number +XMlHttpRequest.LOADING number +XMlHttpRequest.DONE number +XMlHttpRequest.ontimeout object +XMlHttpRequest.onreadystatechange object +XMlHttpRequest.destroyed(QObject*) function +XMlHttpRequest.destroyed() function +XMlHttpRequest.objectNameChanged(QString) function +XMlHttpRequest.deleteLater() function +XMlHttpRequest.requestComplete() function +XMlHttpRequest.abort() function +XMlHttpRequest.setRequestHeader(QString,QString) function +XMlHttpRequest.open(QString,QString,bool,QString,QString) function +XMlHttpRequest.open(QString,QString,bool,QString) function +XMlHttpRequest.open(QString,QString,bool) function +XMlHttpRequest.open(QString,QString) function +XMlHttpRequest.send() function +XMlHttpRequest.send(QScriptValue) function +XMlHttpRequest.getAllResponseHeaders() function +XMlHttpRequest.getResponseHeader(QString) function +*/ + +/**jsdoc + * Provides a means to interact with web servers. It is a near-complete implementation of the XMLHttpRequest API described in + * the Mozilla docs: + * <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest</a>. + * + * @class XMLHttpRequest + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {*} response - The response data. + * <em>Read-only.</em> + * @property {string} responseText - The response data as text. + * <em>Read-only.</em> + * @property {string} responseType - The response type required or received (e.g., <code>"text"</code>, <code>"json"</code>, + * <code>"arraybuffer"</code>, ...). + * @property {number} status - The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status">HTTP response status + * code</a> (<code>100</code> – <code>599</code>). + * <em>Read-only.</em> + * @property {string} statusText - The HTTP response status text. + * <em>Read-only.</em> + * @property {XMLHttpRequest.ReadyState} readyState - The status of the request. + * <em>Read-only.</em> + * @property {XMLHttpRequest.NetworkError} errorCode - The network result of the request: including, <code>0</code> (NoError) + * if there was no error, <code>4</code> (TimeoutError) if the request timed out. + * <em>Read-only.</em> + * @property {number} timeout - The time a request can take before timing out, in ms. + * + * @property {XMLHttpRequest.ReadyState} UNSENT - Request has been created; {@link XMLHttpRequest.open} not called yet. + * <em>Read-only.</em> + * @property {XMLHttpRequest.ReadyState} OPENED - {@link XMLHttpRequest.open} has been called. + * <em>Read-only.</em> + * @property {XMLHttpRequest.ReadyState} HEADERS_RECEIVED - {@link XMLHttpRequest.send} has been called; headers and status + * are available. + * <em>Read-only.</em> + * @property {XMLHttpRequest.ReadyState} LOADING - Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has partial + * data. + * <em>Read-only.</em> + * @property {XMLHttpRequest.ReadyState} DONE - Operation complete. + * <em>Read-only.</em> + * + * @property {XMLHttpRequest~onTimeoutCallback} ontimeout - Function called when the request times out. + * <p>Note: This is called in addition to any function set for <code>onreadystatechange</code>.</p> + * @property {XMLHttpRequest~onReadyStateChangeCallback} onreadystatechange - Function called when the request's ready state + * changes. + * + * @example <caption>Get a web page's HTML.</caption> + * var URL = "https://www.highfidelity.com/"; + * + * var req = new XMLHttpRequest(); + * req.onreadystatechange = function () { + * if (req.readyState === req.DONE) { + * if (req.status === 200) { + * print("Success"); + * print("Content type:", req.getResponseHeader("content-type")); + * print("Content:", req.responseText.slice(0, 100), "..."); + * + * } else { + * print("Error", req.status, req.statusText); + * } + * + * req = null; + * } + * }; + * + * req.open("GET", URL); + * req.send(); + * + * @example <caption>Get a web page's HTML — alternative method.</caption> + * var URL = "https://www.highfidelity.com/"; + * + * var req = new XMLHttpRequest(); + * req.requestComplete.connect(function () { + * if (req.status === 200) { + * print("Success"); + * print("Content type:", req.getResponseHeader("content-type")); + * print("Content:", req.responseText.slice(0, 100), "..."); + * + * } else { + * print("Error", req.status, req.statusText); + * } + * + * req = null; + * }); + * + * req.open("GET", URL); + * req.send(); + */ class XMLHttpRequestClass : public QObject { Q_OBJECT Q_PROPERTY(QScriptValue response READ getResponse) @@ -46,6 +172,26 @@ public: ~XMLHttpRequestClass(); static const int MAXIMUM_REDIRECTS = 5; + + /**jsdoc + * <p>The state of an XMLHttpRequest.</p> + * <table> + * <thead> + * <tr><th>Value</th><th>Name</th><th>Description</th></tr> + * </thead> + * <tbody> + * <tr><td><code>0</code></td><td>UNSENT</td><td>Request has been created; {@link XMLHttpRequest.open} not called + * yet.</td></tr> + * <tr><td><code>1</code></td><td>OPENED</td><td>{@link XMLHttpRequest.open} has been called.</td></tr> + * <tr><td><code>2</code></td><td>HEADERS_RECEIVED</td><td>{@link XMLHttpRequest.send} has been called; headers and + * status are available.</td></tr> + * <tr><td><code>3</code></td><td>LOADING</td><td>Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has + * partial data.</td></tr> + * <tr><td><code>4</code></td><td>DONE</td><td>Operation complete.</td></tr> + * </tbody> + * </table> + * @typedef {number} XMLHttpRequest.ReadyState + */ enum ReadyState { UNSENT = 0, OPENED, @@ -79,16 +225,66 @@ public: void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } public slots: + + /**jsdoc + * Aborts the request. + * @function XMLHttpRequest.abort + */ void abort(); + + /**jsdoc + * Sets the value of an HTTP request header. Must be called after {@link XMLHttpRequest.open} but before + * {@link XMLHttpRequest.send}; + * @function XMLHttpRequest.setRequestHeader + * @param {string} name - The name of the header to set. + * @param {string} value - The value of the header. + */ void setRequestHeader(const QString& name, const QString& value); + + /**jsdoc + * Initializes a request. + * @function XMLHttpRequest.open + * @param {string} method - The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP request method</a> + * to use, e.g., <code>"GET"</code>, <code>"POST"</code>, <code>"PUT"</code>, or <code>"DELETE"</code>. + * @param {string} url - The URL to send the request to. + * @param {boolean} [async=true] - <code>true</code> if the method returns without waiting for the response, + * <code>false</code> if the method returns only once the request is complete. + * @param {string} [username=""] - User name for authentication. + * @param {string} [password=""] - Password for authentication. + */ void open(const QString& method, const QString& url, bool async = true, const QString& username = "", const QString& password = ""); + + /**jsdoc + * Sends the request to the server. + * @function XMLHttpRequest.send + * @param {*} [data] - The data to send. + */ void send(); void send(const QScriptValue& data); + + /**jsdoc + * Gets the response headers. + * @function XMLHttpRequest.getAllResponseHeaders + * @returns {string} The response headers, separated by <code>"\n"</code> characters. + */ QScriptValue getAllResponseHeaders() const; + + /**jsdoc + * Gets a response header. + * @function XMLHttpRequest.getResponseHeader + * @param {string} name - + * @returns {string} The response header. + */ QScriptValue getResponseHeader(const QString& name) const; signals: + + /**jsdoc + * Triggered when the request is complete — with or without error (incl. timeout). + * @function XMLHttpRequest.requestComplete + * @returns {Signal} + */ void requestComplete(); private: @@ -111,7 +307,58 @@ private: QScriptValue _onTimeout { QScriptValue::NullValue }; QScriptValue _onReadyStateChange { QScriptValue::NullValue }; ReadyState _readyState { XMLHttpRequestClass::UNSENT }; + + /**jsdoc + * <p>The type of network error.</p> + * <table> + * <thead> + * <tr><th>Value</th><th>Name</th><th>Description</th></tr> + * </thead> + * <tbody> + * <tr><td><code>0</code></td><td>NoError</td><td>No error.</td></tr> + * <tr><td><code>1</code></td><td>ConnectionRefusedError</td><td>The server refused the connection.</td></tr> + * <tr><td><code>2</code></td><td>RemoteHostClosedError</td><td>The server closed the connection.</td></tr> + * <tr><td><code>3</code></td><td>HostNotFoundError</td><td>Host name not found.</td></tr> + * <tr><td><code>4</code></td><td>TimeoutError</td><td>Connection timed out</td></tr> + * <tr><td><code>5</code></td><td>OperationCanceledError</td><td>Operation canceled by + * {@link XMLHttpRequest.abort}.</td></tr> + * <tr><td><code>6</code></td><td>SslHandshakeFailedError</td><td>SSL/TLS handshake failed.</td></tr> + * <tr><td><code>7</code></td><td>TemporaryNetworkFailureError</td><td>Temporarily disconnected from the + * network.</td></tr> + * <tr><td><code>8</code></td><td>NetworkSessionFailedError</td><td>Disconnection from the network.</td></tr> + * <tr><td><code>9</code></td><td>BackgroundRequestNotAllowedError</td><td>Background request not allowed.</td></tr> + * <tr><td><code>10</code></td><td>TooManyRedirectsError</td><td>Too many redirects.</td></tr> + * <tr><td><code>11</code></td><td>InsecureRedirectError</td><td>Redirect from secure to insecure protocol.</td></tr> + * <tr><td><code>101</code></td><td>ProxyConnectionRefusedError</td><td>Connection to proxy server refused.</td></tr> + * <tr><td><code>102</code></td><td>ProxyConnectionClosedError</td><td>Proxy server closed the connection.</td></tr> + * <tr><td><code>103</code></td><td>ProxyNotFoundError</td><td>Proxy host name not found.</td></tr> + * <tr><td><code>104</code></td><td>ProxyTimeoutError</td><td>Proxy connection timed out.</td></tr> + * <tr><td><code>105</code></td><td>ProxyAuthenticationRequiredError</td><td>Proxy requires authentication.</td></tr> + * <tr><td><code>201</code></td><td>ContentAccessDenied</td><td>Access denied.</td></tr> + * <tr><td><code>202</code></td><td>ContentOperationNotPermittedError</td><td>Operation not permitted.</td></tr> + * <tr><td><code>203</code></td><td>ContentNotFoundError</td><td>Content not found.</td></tr> + * <tr><td><code>204</code></td><td>AuthenticationRequiredError</td><td>Authentication required.</td></tr> + * <tr><td><code>205</code></td><td>ContentReSendError</td><td>Resend failed.</td></tr> + * <tr><td><code>206</code></td><td>ContentConflictError</td><td>Resource state conflict.</td></tr> + * <tr><td><code>207</code></td><td>ContentGoneError</td><td>Resource no longer available.</td></tr> + * <tr><td><code>401</code></td><td>InternalServerError</td><td>Internal server error.</td></tr> + * <tr><td><code>402</code></td><td>OperationNotImplementedError</td><td>Operation not supported.</td></tr> + * <tr><td><code>403</code></td><td>ServiceUnavailableError</td><td>Request not able to be handled at this + * time.</td></tr> + * <tr><td><code>301</code></td><td>ProtocolUnknownError</td><td>Protocol unknown.</td></tr> + * <tr><td><code>302</code></td><td>ProtocolInvalidOperationError</td><td>Operation invalid fro protocol.</td></tr> + * <tr><td><code>99</code></td><td>UnknownNetworkError</td><td>Unknown network-related error.</td></tr> + * <tr><td><code>199</code></td><td>UnknownProxyError</td><td>Unknown proxy-related error.</td></tr> + * <tr><td><code>299</code></td><td>UnknownContentError</td><td>Unknown content-related error.</td></tr> + * <tr><td><code>399</code></td><td>ProtocolFailure</td><td>Protocol error.</td></tr> + * <tr><td><code>499</code></td><td>UnknownServerError</td><td>Unknown server response error.</td></tr> + + * </tbody> + * </table> + * @typedef {number} XMLHttpRequest.NetworkError + */ QNetworkReply::NetworkError _errorCode { QNetworkReply::NoError }; + int _timeout { 0 }; QTimer _timer; int _numRedirects { 0 }; From 3aaeba76a9c6585bbf82f98dc0f1e4c122926ccc Mon Sep 17 00:00:00 2001 From: David Rowe <david@ctrlaltstudio.com> Date: Tue, 17 Dec 2019 09:20:18 +1300 Subject: [PATCH 2/2] Fix typos in WebSocket JSDoc --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- libraries/script-engine/src/WebSocketClass.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 73774912a9..7333e38037 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -99,7 +99,7 @@ void WebSocketClass::handleOnClose() { * @param {WebSocket.SocketError} error - The error. */ /**jsdoc - * Information on a socket error. + * <p>The type of socket error.</p> * <table> * <thead> * <tr><th>Value</th><th>Name</th><th>Description</th></tr> diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 56fb334152..c181965bf7 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -17,9 +17,9 @@ #include <QWebSocket> /**jsdoc - * Provides a bi-direcctional, event-driven communication session between the script and another WebSocket connection. It is a + * Provides a bi-directional, event-driven communication session between the script and another WebSocket connection. It is a * near-complete implementation of the WebSocket API described in the Mozilla docs: - * <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">https://developer.mozilla.org/en-US/docs/Web/API/WebSocket</a>.</p> + * <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">https://developer.mozilla.org/en-US/docs/Web/API/WebSocket</a>. * * <p>Constructed by <code>new WebSocket(...)</code> in Interface, client entity, avatar, and server entity scripts, or the * {@link WebSocketServer} class in server entity and assignment client scripts.