Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: src/WebRequestJsObject.cpp

Issue 29369520: Issue #4692 - Rewrite web request task to avoid engine self-reference
Patch Set: Created Dec. 24, 2016, 4:58 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/WebRequestJsObject.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
}
« no previous file with comments | « src/WebRequestJsObject.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld