Index: src/WebRequestJsObject.cpp |
=================================================================== |
--- a/src/WebRequestJsObject.cpp |
+++ b/src/WebRequestJsObject.cpp |
@@ -19,9 +19,12 @@ |
#include <AdblockPlus/JsValue.h> |
#include <AdblockPlus/WebRequest.h> |
-#include "JsContext.h" |
+#include "JsEngineInternal.h" |
+#include "JsEngineTransition.h" |
#include "Utils.h" |
#include "Scheduler.h" |
+#include "V8Upgrade.h" |
+#include "Value.h" |
#include "WebRequestJsObject.h" |
namespace |
@@ -29,90 +32,138 @@ |
class WebRequestTask |
{ |
public: |
- WebRequestTask(AdblockPlus::JsEnginePtr jsEngine, AdblockPlus::JsValueList& arguments) |
- : jsEngine(jsEngine), url(arguments[0]->AsString()) |
- { |
- if (!url.length()) |
- throw std::runtime_error("Invalid string passed as first argument to GET"); |
- |
- { |
- AdblockPlus::JsValuePtr headersObj = arguments[1]; |
- if (!headersObj->IsObject()) |
- throw std::runtime_error("Second argument to GET must be an object"); |
- |
- std::vector<std::string> properties = headersObj->GetOwnPropertyNames(); |
- for (std::vector<std::string>::iterator it = properties.begin(); |
- it != properties.end(); ++it) |
- { |
- std::string header = *it; |
- std::string headerValue = headersObj->GetProperty(header)->AsString(); |
- if (header.length() && headerValue.length()) |
- headers.push_back(std::pair<std::string, std::string>(header, headerValue)); |
- } |
- } |
- |
- callback = arguments[2]; |
- if (!callback->IsFunction()) |
- throw std::runtime_error("Third argument to GET must be a function"); |
- } |
+ WebRequestTask( |
+ JsEngineInternal *engine, |
+ std::string url, |
+ AdblockPlus::HeaderList headers, |
+ V8PersistentNG<v8::Function> callbackFunction |
+ ) |
+ : jsEngine(engine->shared_from_this()), |
+ url(url), headers(headers), callbackFunction(callbackFunction) |
+ {} |
void operator()() |
{ |
- AdblockPlus::ServerResponse result = jsEngine->GetWebRequest()->GET(url, headers); |
- |
- AdblockPlus::JsContext context(jsEngine); |
- |
- AdblockPlus::JsValuePtr resultObject = jsEngine->NewObject(); |
- resultObject->SetProperty("status", result.status); |
- resultObject->SetProperty("responseStatus", result.responseStatus); |
- resultObject->SetProperty("responseText", result.responseText); |
+ auto engine = ToInternal(jsEngine); // temporary statement while task keeps its own engine alive |
+ /* |
+ * Synchronous HTTP GET request is arbitrary-duration and not interruptible |
+ */ |
+ AdblockPlus::ServerResponse result = engine->GetWebRequest()->GET(url, headers); |
+ /* |
+ * Instantiate our scope after the long-lived operation above. |
+ */ |
+ V8ExecutionScope sentry(engine); |
+ auto isolate = engine->GetIsolate(); |
- AdblockPlus::JsValuePtr headersObject = jsEngine->NewObject(); |
- for (AdblockPlus::HeaderList::iterator it = result.responseHeaders.begin(); |
- it != result.responseHeaders.end(); ++it) |
+ // Create the response object to pass to the callback function |
+ auto response = v8::Object::New(); |
+ SetPropertyOnV8Object(isolate, response, "status", result.status); |
+ SetPropertyOnV8Object(isolate, response, "responseStatus", result.responseStatus); |
+ SetPropertyOnV8Object(isolate, response, "responseText", result.responseText); |
+ auto responseHeaders = v8::Object::New(); |
+ for (auto it = result.responseHeaders.begin(); it != result.responseHeaders.end(); ++it) |
{ |
- headersObject->SetProperty(it->first, it->second); |
+ SetPropertyOnV8Object(isolate, responseHeaders, it->first, it->second); |
} |
- resultObject->SetProperty("responseHeaders", headersObject); |
+ SetPropertyOnV8Object(isolate, response, "responseHeaders", responseHeaders); |
- AdblockPlus::JsValueList params; |
- params.push_back(resultObject); |
- callback->Call(params); |
+ // Call the callback |
+ auto args = AllocatedArray<v8::Local<v8::Value>>(1); |
+ args[0] = response; |
+ engine->ApplyFunction(callbackFunction.Get(isolate), std::move(args)); |
} |
private: |
+ /** |
+ * Engine pointer keeps engine in existence across thread boundary |
+ */ |
AdblockPlus::JsEnginePtr jsEngine; |
std::string url; |
AdblockPlus::HeaderList headers; |
- AdblockPlus::JsValuePtr callback; |
+ V8PersistentNG<v8::Function> callbackFunction; |
}; |
- |
- v8::Handle<v8::Value> GETCallback(const v8::Arguments& arguments) |
- { |
- std::shared_ptr<WebRequestTask> thread; |
- AdblockPlus::JsEnginePtr jsEngine; |
- try |
- { |
- jsEngine = AdblockPlus::JsEngine::FromArguments(arguments); |
- AdblockPlus::JsValueList converted = jsEngine->ConvertArguments(arguments); |
- if (converted.size() != 3u) |
- throw std::runtime_error("GET requires exactly 3 arguments"); |
- thread = std::make_shared<WebRequestTask>(jsEngine, converted); |
- } |
- catch (const std::exception& e) |
- { |
- using AdblockPlus::Utils::ToV8String; |
- v8::Isolate* isolate = arguments.GetIsolate(); |
- return v8::ThrowException(ToV8String(isolate, e.what())); |
- } |
- jsEngine->Schedule(AdblockPlus::MakeHeapFunction(thread), AdblockPlus::ImmediateSingleUseThread); |
- return v8::Undefined(); |
- } |
} |
-AdblockPlus::JsValuePtr AdblockPlus::WebRequestJsObject::Setup( |
- AdblockPlus::JsEnginePtr jsEngine, AdblockPlus::JsValuePtr obj) |
+/** |
+ * Implementing function for JS "<global>._webrequest.GET()". |
+ * |
+ * Used in "compat.js" to implement "XMLHttpRequest.send()" for GET requests. |
+ * |
+ * \par JavaScript arguments |
+ * 1. url. |
+ * The URL of the resource to which the request will be sent. |
+ * 2. requestHeaders. |
+ * An object whose properties represent HTTP GET request headers. |
+ * These properties are added to the GET request. |
+ * 3. readyCallback. |
+ * A function to be executed when the GET response is ready. |
+ * The argument of this call is an object encoding the GET response. |
+ */ |
+v8::Handle<v8::Value> GETCallback(const v8::Arguments& arguments) |
{ |
- obj->SetProperty("GET", jsEngine->NewCallback(::GETCallback)); |
- return obj; |
+ auto engine = JsEngineInternal::ExtractEngine(arguments); |
+ V8ExecutionScope sentry(engine); |
+ |
+ /* |
+ * Factory block for WebRequest tasks. |
+ */ |
+ std::shared_ptr<WebRequestTask> task; |
+ try |
+ { |
+ std::string url; |
+ if (arguments.Length() != 3u) |
+ { |
+ throw std::runtime_error("GET requires exactly 3 arguments"); |
+ } |
+ bool b; |
+ std::tie(b, url) = ConvertString(arguments[0]); |
+ if (!b) |
+ { |
+ throw std::runtime_error("First argument to GET must be a string"); |
+ } |
+ if (!url.length()) |
+ { |
+ throw std::runtime_error("Invalid string passed as first argument to GET"); |
+ } |
+ |
+ AdblockPlus::HeaderList headers; |
+ auto headersArg = arguments[1]; |
+ if (!headersArg->IsObject()) |
+ { |
+ throw std::runtime_error("Second argument to GET must be an object"); |
+ } |
+ auto headersObj = v8::Local<v8::Object>::Cast(headersArg); |
+ auto headersProperties = headersObj->GetOwnPropertyNames(); |
+ uint32_t length = headersProperties->Length(); |
+ for (uint32_t i = 0; i < length; i++) |
+ { |
+ auto v8PropertyName(headersProperties->Get(i)); |
+ auto v8PropertyValue(headersObj->Get(v8PropertyName)); |
+ auto propertyName(AdblockPlus::Utils::FromV8String(v8PropertyName)); |
+ auto propertyValue(AdblockPlus::Utils::FromV8String(v8PropertyValue)); |
+ if (propertyName.length() > 0 && propertyValue.length() > 0) |
+ { |
+ headers.push_back(std::make_pair(propertyName, propertyValue)); |
+ } |
+ } |
+ |
+ auto callbackArg = arguments[2]; |
+ if (!callbackArg->IsFunction()) |
+ { |
+ throw std::runtime_error("Third argument to GET must be a function"); |
+ } |
+ task = std::make_shared<WebRequestTask>(engine, url, headers, |
+ V8PersistentNG<v8::Function>(engine->GetIsolate(), v8::Local<v8::Function>::Cast(arguments[2]))); |
+ } |
+ catch (const std::exception& e) |
+ { |
+ using AdblockPlus::Utils::ToV8String; |
+ v8::Isolate* isolate = arguments.GetIsolate(); |
+ return v8::ThrowException(ToV8String(isolate, e.what())); |
+ } |
+ /* |
+ * Run the task |
+ */ |
+ engine->Schedule(AdblockPlus::MakeHeapFunction(task), AdblockPlus::ImmediateSingleUseThread); |
+ return v8::Undefined(); |
} |