mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-05 13:47:26 +02:00
Implement Tomahawk.ajax native implementation on behalf of NativeScriptJobs
This commit is contained in:
@@ -430,6 +430,23 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
|
|||||||
delete Tomahawk.asyncRequestCallbacks[reqId];
|
delete Tomahawk.asyncRequestCallbacks[reqId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is externalized from Tomahawk.asyncRequest, so that other clients
|
||||||
|
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
|
||||||
|
* natively.
|
||||||
|
*
|
||||||
|
* @returns boolean indicating whether or not to do a request with the given parameters natively
|
||||||
|
*/
|
||||||
|
var shouldDoNativeRequest = function (options) {
|
||||||
|
var extraHeaders = options.headers;
|
||||||
|
return (extraHeaders && (extraHeaders.hasOwnProperty("Referer")
|
||||||
|
|| extraHeaders.hasOwnProperty("referer")
|
||||||
|
|| extraHeaders.hasOwnProperty("User-Agent")));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible options:
|
* Possible options:
|
||||||
* - method: The HTTP request method (default: GET)
|
* - method: The HTTP request method (default: GET)
|
||||||
@@ -439,59 +456,45 @@ Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
|
|||||||
* - data: body data included in POST requests
|
* - data: body data included in POST requests
|
||||||
* - needCookieHeader: boolean indicating whether or not the request needs to be able to get the
|
* - needCookieHeader: boolean indicating whether or not the request needs to be able to get the
|
||||||
* "Set-Cookie" response header
|
* "Set-Cookie" response header
|
||||||
|
* - headers: headers set on the request
|
||||||
*/
|
*/
|
||||||
Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) {
|
var doRequest = function(options) {
|
||||||
// unpack options
|
if (shouldDoNativeRequest(options)) {
|
||||||
var opt = options || {};
|
return Tomahawk.NativeScriptJobManager.invoke('httpRequest', options).then(function(xhr) {
|
||||||
var method = opt.method || 'GET';
|
xhr.responseHeaders = xhr.responseHeaders || {};
|
||||||
|
xhr.getAllResponseHeaders = function() {
|
||||||
|
return this.responseHeaders;
|
||||||
|
};
|
||||||
|
xhr.getResponseHeader = function (header) {
|
||||||
|
return this.responseHeaders[header];
|
||||||
|
};
|
||||||
|
|
||||||
if (shouldDoNativeRequest(url, callback, extraHeaders, options)) {
|
return xhr;
|
||||||
// Assign a request Id to the callback so we can use it when we are
|
});
|
||||||
// returning from the native call.
|
|
||||||
var reqId = Tomahawk.asyncRequestIdCounter;
|
|
||||||
Tomahawk.asyncRequestIdCounter++;
|
|
||||||
Tomahawk.asyncRequestCallbacks[reqId] = {
|
|
||||||
callback: callback,
|
|
||||||
errorHandler: opt.errorHandler
|
|
||||||
};
|
|
||||||
Tomahawk.nativeAsyncRequest(reqId, url, extraHeaders, options);
|
|
||||||
} else {
|
} else {
|
||||||
var xmlHttpRequest = new XMLHttpRequest();
|
return new RSVP.Promise(function(resolve, reject) {
|
||||||
xmlHttpRequest.open(method, url, true, opt.username, opt.password);
|
var xmlHttpRequest = new XMLHttpRequest();
|
||||||
if (extraHeaders) {
|
xmlHttpRequest.open(options.method, options.url, true, options.username, options.password);
|
||||||
for (var headerName in extraHeaders) {
|
if (options.headers) {
|
||||||
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
|
for (var headerName in options.headers) {
|
||||||
}
|
xmlHttpRequest.setRequestHeader(headerName, options.headers[headerName]);
|
||||||
}
|
|
||||||
xmlHttpRequest.onreadystatechange = function () {
|
|
||||||
if (xmlHttpRequest.readyState == 4
|
|
||||||
&& httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
|
|
||||||
callback.call(window, xmlHttpRequest);
|
|
||||||
} else if (xmlHttpRequest.readyState === 4) {
|
|
||||||
Tomahawk.log("Failed to do " + method + " request: to: " + url);
|
|
||||||
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
|
|
||||||
if (opt.hasOwnProperty('errorHandler')) {
|
|
||||||
opt.errorHandler.call(window, xmlHttpRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
xmlHttpRequest.onreadystatechange = function () {
|
||||||
xmlHttpRequest.send(opt.data || null);
|
if (xmlHttpRequest.readyState == 4
|
||||||
|
&& httpSuccessStatuses.indexOf(xmlHttpRequest.status) != -1) {
|
||||||
|
resolve(xmlHttpRequest);
|
||||||
|
} else if (xmlHttpRequest.readyState === 4) {
|
||||||
|
Tomahawk.log("Failed to do " + options.method + " request: to: " + options.url);
|
||||||
|
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
|
||||||
|
reject(xmlHttpRequest);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xmlHttpRequest.send(options.data || null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is externalized from Tomahawk.asyncRequest, so that other clients
|
|
||||||
* (like tomahawk-android) can inject their own logic that determines whether or not to do a request
|
|
||||||
* natively.
|
|
||||||
*
|
|
||||||
* @returns boolean indicating whether or not to do a request with the given parameters natively
|
|
||||||
*/
|
|
||||||
var shouldDoNativeRequest = function (url, callback, extraHeaders, options) {
|
|
||||||
return (extraHeaders && (extraHeaders.hasOwnProperty("Referer")
|
|
||||||
|| extraHeaders.hasOwnProperty("referer")
|
|
||||||
|| extraHeaders.hasOwnProperty("User-Agent")));
|
|
||||||
};
|
|
||||||
|
|
||||||
Tomahawk.ajax = function (url, settings) {
|
Tomahawk.ajax = function (url, settings) {
|
||||||
if (typeof url === "object") {
|
if (typeof url === "object") {
|
||||||
settings = url;
|
settings = url;
|
||||||
@@ -549,10 +552,7 @@ Tomahawk.ajax = function (url, settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RSVP.Promise(function (resolve, reject) {
|
return doRequest(settings).then(function (xhr) {
|
||||||
settings.errorHandler = reject;
|
|
||||||
Tomahawk.asyncRequest(settings.url, resolve, settings.headers, settings);
|
|
||||||
}).then(function (xhr) {
|
|
||||||
if (settings.rawResponse) {
|
if (settings.rawResponse) {
|
||||||
return xhr;
|
return xhr;
|
||||||
}
|
}
|
||||||
@@ -876,12 +876,17 @@ Tomahawk.PluginManager = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var encodeParamsToNativeFunctions = function(param) {
|
||||||
|
return param;
|
||||||
|
};
|
||||||
|
|
||||||
Tomahawk.NativeScriptJobManager = {
|
Tomahawk.NativeScriptJobManager = {
|
||||||
idCounter: 0,
|
idCounter: 0,
|
||||||
deferreds: {},
|
deferreds: {},
|
||||||
invoke: function (methodName, params) {
|
invoke: function (methodName, params) {
|
||||||
var requestId = this.idCounter++;
|
var requestId = this.idCounter++;
|
||||||
Tomahawk.invokeNativeScriptJob(requestId, methodName, JSON.stringify(params));
|
Tomahawk.invokeNativeScriptJob(requestId, methodName, encodeParamsToNativeFunctions(params));
|
||||||
this.deferreds[requestId] = RSVP.defer();
|
this.deferreds[requestId] = RSVP.defer();
|
||||||
return this.deferreds[requestId].promise;
|
return this.deferreds[requestId].promise;
|
||||||
},
|
},
|
||||||
@@ -891,6 +896,7 @@ Tomahawk.NativeScriptJobManager = {
|
|||||||
Tomahawk.log("Deferred object with the given requestId is not present!");
|
Tomahawk.log("Deferred object with the given requestId is not present!");
|
||||||
}
|
}
|
||||||
deferred.resolve(result);
|
deferred.resolve(result);
|
||||||
|
delete this.deferreds[requestId];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -170,6 +170,22 @@ JSAccount::syncInvoke( const scriptobject_ptr& scriptObject, const QString& meth
|
|||||||
return evaluateJavaScriptWithResult( eval );
|
return evaluateJavaScriptWithResult( eval );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSAccount::reportNativeScriptJobResult( int resultId, const QVariantMap& result )
|
||||||
|
{
|
||||||
|
QString eval = QString(
|
||||||
|
"Tomahawk.NativeScriptJobManager.reportNativeScriptJobResult("
|
||||||
|
"%1," // requestId
|
||||||
|
"%2" // results
|
||||||
|
");"
|
||||||
|
).arg( resultId )
|
||||||
|
.arg( serializeQVariantMap( result ) );
|
||||||
|
|
||||||
|
// Remove when new scripting api turned out to work reliably
|
||||||
|
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
|
||||||
|
|
||||||
|
evaluateJavaScript( eval );
|
||||||
|
}
|
||||||
|
|
||||||
QVariant
|
QVariant
|
||||||
JSAccount::evaluateJavaScriptInternal( const QString& scriptSource )
|
JSAccount::evaluateJavaScriptInternal( const QString& scriptSource )
|
||||||
|
@@ -71,6 +71,8 @@ public:
|
|||||||
|
|
||||||
static QString serializeQVariantMap(const QVariantMap& map);
|
static QString serializeQVariantMap(const QVariantMap& map);
|
||||||
|
|
||||||
|
void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods
|
* Wrap the pure evaluateJavaScript call in here, while the threadings guards are in public methods
|
||||||
|
@@ -47,6 +47,8 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QWebFrame>
|
#include <QWebFrame>
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
#include <taglib/asffile.h>
|
#include <taglib/asffile.h>
|
||||||
#include <taglib/flacfile.h>
|
#include <taglib/flacfile.h>
|
||||||
#include <taglib/id3v2framefactory.h>
|
#include <taglib/id3v2framefactory.h>
|
||||||
@@ -627,12 +629,24 @@ JSResolverHelper::nativeRetrieveMetadata( int metadataId, const QString& url,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JSResolverHelper::invokeNativeScriptJob( int requestId, const QString& methodName, const QVariantMap& params )
|
||||||
|
{
|
||||||
|
if ( methodName == "httpRequest" ) {
|
||||||
|
nativeAsyncRequest( requestId, params );
|
||||||
|
} else {
|
||||||
|
// TODO: make promise reject instead
|
||||||
|
Q_ASSERT_X(false, "invokeNativeScriptJob", "NativeScriptJob methodName was not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
|
JSResolverHelper::nativeAsyncRequest( const int requestId, const QVariantMap& options )
|
||||||
const QVariantMap& headers,
|
|
||||||
const QVariantMap& options )
|
|
||||||
{
|
{
|
||||||
|
QString url = options[ "url" ].toString();
|
||||||
|
QVariantMap headers = options[ "headers" ].toMap();
|
||||||
|
|
||||||
QNetworkRequest req( url );
|
QNetworkRequest req( url );
|
||||||
foreach ( const QString& key, headers.keys() )
|
foreach ( const QString& key, headers.keys() )
|
||||||
{
|
{
|
||||||
@@ -688,17 +702,16 @@ JSResolverHelper::nativeAsyncRequestDone( int requestId, NetworkReply* reply )
|
|||||||
map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
|
map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
|
||||||
map["statusText"] = QString("%1 %2").arg( map["status"].toString() )
|
map["statusText"] = QString("%1 %2").arg( map["status"].toString() )
|
||||||
.arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() );
|
.arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() );
|
||||||
if (reply->reply()->hasRawHeader( "Content-Type" ))
|
|
||||||
map["contentType"] = reply->reply()->rawHeader( "Content-Type" );
|
|
||||||
|
|
||||||
bool ok = false;
|
|
||||||
QString json = QString::fromUtf8( TomahawkUtils::toJson( map, &ok ) );
|
|
||||||
Q_ASSERT( ok );
|
|
||||||
|
|
||||||
QString javascript = QString( "Tomahawk.nativeAsyncRequestDone( %1, %2 );" )
|
QVariantMap responseHeaders;
|
||||||
.arg( QString::number( requestId ) )
|
foreach( const QNetworkReply::RawHeaderPair& pair, reply->reply()->rawHeaderPairs() )
|
||||||
.arg( json );
|
{
|
||||||
m_resolver->d_func()->scriptAccount->evaluateJavaScript( javascript );
|
responseHeaders[ pair.first ] = pair.second;
|
||||||
|
}
|
||||||
|
map["responseHeaders"] = responseHeaders;
|
||||||
|
|
||||||
|
m_resolver->d_func()->scriptAccount->reportNativeScriptJobResult( requestId, map );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -86,20 +86,9 @@ public:
|
|||||||
int sizehint,
|
int sizehint,
|
||||||
const QVariantMap& options );
|
const QVariantMap& options );
|
||||||
|
|
||||||
/**
|
Q_INVOKABLE void invokeNativeScriptJob( int requestId,
|
||||||
* Native handler for asynchronous HTTP requests.
|
const QString& methodName,
|
||||||
*
|
const QVariantMap& params );
|
||||||
* This handler shall only be used if we cannot achieve the request with
|
|
||||||
* XMLHttpRequest as that would be more efficient.
|
|
||||||
* Use cases are:
|
|
||||||
* * Referer header: Stripped on MacOS and the specification says it
|
|
||||||
* should be stripped
|
|
||||||
*
|
|
||||||
* INTERNAL USE ONLY!
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void nativeAsyncRequest( int requestId, const QString& url,
|
|
||||||
const QVariantMap& headers,
|
|
||||||
const QVariantMap& options );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lucene++ indices for JS resolvers
|
* Lucene++ indices for JS resolvers
|
||||||
@@ -145,6 +134,10 @@ private:
|
|||||||
bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData );
|
bool indexDataFromVariant( const QVariantMap& map, struct Tomahawk::IndexData& indexData );
|
||||||
QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query );
|
QVariantList searchInFuzzyIndex( const Tomahawk::query_ptr& query );
|
||||||
|
|
||||||
|
// native script jobs
|
||||||
|
void nativeAsyncRequest( int requestId, const QVariantMap& options );
|
||||||
|
|
||||||
|
|
||||||
QVariantMap m_resolverConfig;
|
QVariantMap m_resolverConfig;
|
||||||
JSResolver* m_resolver;
|
JSResolver* m_resolver;
|
||||||
QString m_scriptPath;
|
QString m_scriptPath;
|
||||||
|
@@ -65,13 +65,14 @@ public:
|
|||||||
|
|
||||||
ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments );
|
ScriptJob* invoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments );
|
||||||
virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
|
virtual QVariant syncInvoke( const scriptobject_ptr& scriptObject, const QString& methodName, const QVariantMap& arguments ) = 0;
|
||||||
|
|
||||||
virtual void startJob( ScriptJob* scriptJob ) = 0;
|
virtual void startJob( ScriptJob* scriptJob ) = 0;
|
||||||
|
|
||||||
void reportScriptJobResult( const QVariantMap& result );
|
void reportScriptJobResult( const QVariantMap& result );
|
||||||
void registerScriptPlugin( const QString& type, const QString& objectId );
|
void registerScriptPlugin( const QString& type, const QString& objectId );
|
||||||
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
void unregisterScriptPlugin( const QString& type, const QString& objectId );
|
||||||
|
|
||||||
|
virtual void reportNativeScriptJobResult( int resultId, const QVariantMap& result ) = 0;
|
||||||
|
|
||||||
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
|
virtual void scriptPluginFactory( const QString& type, const scriptobject_ptr& object );
|
||||||
|
|
||||||
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
|
QList< Tomahawk::result_ptr > parseResultVariantList( const QVariantList& reslist );
|
||||||
|
Reference in New Issue
Block a user