summaryrefslogtreecommitdiffstats
path: root/net/proxy/proxy_resolver_v8.cc
diff options
context:
space:
mode:
authorericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-02 21:45:18 +0000
committerericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-02 21:45:18 +0000
commit50d7d7283db42b531a9df9c6f5a34b8526d4d5b0 (patch)
tree6d29abd0b0a82a658a9a526e37279a26a636142e /net/proxy/proxy_resolver_v8.cc
parente97cdd5e81a3f8c3bd735ab1e7d264c36c2c3870 (diff)
downloadchromium_src-50d7d7283db42b531a9df9c6f5a34b8526d4d5b0.zip
chromium_src-50d7d7283db42b531a9df9c6f5a34b8526d4d5b0.tar.gz
chromium_src-50d7d7283db42b531a9df9c6f5a34b8526d4d5b0.tar.bz2
Add js bindings layer for ProxyResolverV8 {"alert()", "dnsResolve()", "myIpAddress" <-- [partial]}.
Also adds a utility function to net_util for turning a addrinfo into an IP address string. BUG=2764 Review URL: http://codereview.chromium.org/31009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10730 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy/proxy_resolver_v8.cc')
-rw-r--r--net/proxy/proxy_resolver_v8.cc200
1 files changed, 184 insertions, 16 deletions
diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc
index f26a6f6..09472e9 100644
--- a/net/proxy/proxy_resolver_v8.cc
+++ b/net/proxy/proxy_resolver_v8.cc
@@ -4,21 +4,23 @@
#include "net/proxy/proxy_resolver_v8.h"
+#include "base/logging.h"
+#include "net/base/address_list.h"
+#include "net/base/host_resolver.h"
#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
#include "net/proxy/proxy_resolver_script.h"
#include "v8/include/v8.h"
-// TODO(eroman):
-// - javascript binding for "alert()"
-// - javascript binding for "myIpAddress()"
-// - javascript binding for "dnsResolve()"
-// - log errors for PAC authors (like if "FindProxyForURL" is missing, or
-// the javascript was malformed, etc..
namespace net {
namespace {
+// Pseudo-name for the PAC script.
+const char kPacResourceName[] = "proxy-pac-script.js";
+
+// Convert a V8 String to a std::string.
std::string V8StringToStdString(v8::Handle<v8::String> s) {
int len = s->Utf8Length();
std::string result;
@@ -26,24 +28,86 @@ std::string V8StringToStdString(v8::Handle<v8::String> s) {
return result;
}
+// Convert a std::string to a V8 string.
v8::Local<v8::String> StdStringToV8String(const std::string& s) {
return v8::String::New(s.data(), s.size());
}
+// String-ize a V8 object by calling its toString() method. Returns true
+// on success. This may fail if the toString() throws an exception.
+bool V8ObjectToString(v8::Handle<v8::Value> object, std::string* result) {
+ if (object.IsEmpty())
+ return false;
+
+ v8::HandleScope scope;
+ v8::Local<v8::String> str_object = object->ToString();
+ if (str_object.IsEmpty())
+ return false;
+ *result = V8StringToStdString(str_object);
+ return true;
+}
+
+// JSBIndings implementation.
+class DefaultJSBindings : public ProxyResolverV8::JSBindings {
+ public:
+ // Handler for "alert(message)".
+ virtual void Alert(const std::string& message) {
+ LOG(INFO) << "PAC-alert: " << message;
+ }
+
+ // Handler for "myIpAddress()". Returns empty string on failure.
+ virtual std::string MyIpAddress() {
+ // TODO(eroman):
+ NOTIMPLEMENTED();
+ return "127.0.0.1";
+ }
+
+ // Handler for "dnsResolve(host)". Returns empty string on failure.
+ virtual std::string DnsResolve(const std::string& host) {
+ // Try to resolve synchronously.
+ net::AddressList address_list;
+ const int kPort = 80; // Doesn't matter what this is.
+ int result = host_resolver_.Resolve(host, kPort, &address_list, NULL);
+
+ if (result != OK)
+ return std::string(); // Failed.
+
+ if (!address_list.head())
+ return std::string();
+
+ // There may be multiple results; we will just use the first one.
+ // This returns empty string on failure.
+ return net::NetAddressToString(address_list.head());
+ }
+
+ // Handler for when an error is encountered. |line_number| may be -1.
+ virtual void OnError(int line_number, const std::string& message) {
+ if (line_number == -1)
+ LOG(INFO) << "PAC-error: " << message;
+ else
+ LOG(INFO) << "PAC-error: " << "line: " << line_number << ": " << message;
+ }
+
+ private:
+ HostResolver host_resolver_;
+};
+
} // namespace
// ProxyResolverV8::Context ---------------------------------------------------
class ProxyResolverV8::Context {
public:
- explicit Context(const std::string& pac_data) {
+ Context(JSBindings* js_bindings, const std::string& pac_data)
+ : js_bindings_(js_bindings) {
+ DCHECK(js_bindings != NULL);
InitV8(pac_data);
}
~Context() {
v8::Locker locked;
- v8::HandleScope scope;
+ v8_this_.Dispose();
v8_context_.Dispose();
}
@@ -55,23 +119,29 @@ class ProxyResolverV8::Context {
v8::Local<v8::Value> function =
v8_context_->Global()->Get(v8::String::New("FindProxyForURL"));
- if (!function->IsFunction())
+ if (!function->IsFunction()) {
+ js_bindings_->OnError(-1, "FindProxyForURL() is undefined.");
return ERR_PAC_SCRIPT_FAILED;
+ }
v8::Handle<v8::Value> argv[] = {
StdStringToV8String(query_url.spec()),
StdStringToV8String(query_url.host()),
};
+ v8::TryCatch try_catch;
v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
v8_context_->Global(), arraysize(argv), argv);
- // The handle is empty if an exception was thrown from "FindProxyForURL".
- if (ret.IsEmpty())
+ if (try_catch.HasCaught()) {
+ HandleError(try_catch.Message());
return ERR_PAC_SCRIPT_FAILED;
+ }
- if (!ret->IsString())
+ if (!ret->IsString()) {
+ js_bindings_->OnError(-1, "FindProxyForURL() did not return a string.");
return ERR_PAC_SCRIPT_FAILED;
+ }
std::string ret_str = V8StringToStdString(ret->ToString());
@@ -85,20 +155,110 @@ class ProxyResolverV8::Context {
v8::Locker locked;
v8::HandleScope scope;
+ v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+ // Attach the javascript bindings.
+ v8::Local<v8::FunctionTemplate> alert_template =
+ v8::FunctionTemplate::New(&AlertCallback, v8_this_);
+ global_template->Set(v8::String::New("alert"), alert_template);
+
+ v8::Local<v8::FunctionTemplate> my_ip_address_template =
+ v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
+ global_template->Set(v8::String::New("myIpAddress"),
+ my_ip_address_template);
+
+ v8::Local<v8::FunctionTemplate> dns_resolve_template =
+ v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
+ global_template->Set(v8::String::New("dnsResolve"),
+ dns_resolve_template);
+
v8_context_ = v8::Context::New(NULL, global_template);
+
v8::Context::Scope ctx(v8_context_);
- std::string text_raw = pac_data + PROXY_RESOLVER_SCRIPT;
+ v8::TryCatch try_catch;
+ // Compile the script, including the PAC library functions.
+ std::string text_raw = pac_data + PROXY_RESOLVER_SCRIPT;
v8::Local<v8::String> text = StdStringToV8String(text_raw);
- v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New(""));
+ v8::ScriptOrigin origin = v8::ScriptOrigin(
+ v8::String::New(kPacResourceName));
v8::Local<v8::Script> code = v8::Script::Compile(text, &origin);
+
+ // Execute.
if (!code.IsEmpty())
code->Run();
+
+ if (try_catch.HasCaught())
+ HandleError(try_catch.Message());
}
+ // Handle an exception thrown by V8.
+ void HandleError(v8::Handle<v8::Message> message) {
+ if (message.IsEmpty())
+ return;
+
+ // Otherwise dispatch to the bindings.
+ int line_number = message->GetLineNumber();
+ std::string error_message;
+ V8ObjectToString(message->Get(), &error_message);
+ js_bindings_->OnError(line_number, error_message);
+ }
+
+ // V8 callback for when "alert()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> AlertCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ // Like firefox we assume "undefined" if no argument was specified, and
+ // disregard any arguments beyond the first.
+ std::string message;
+ if (args.Length() == 0) {
+ message = "undefined";
+ } else {
+ if (!V8ObjectToString(args[0], &message))
+ return v8::Undefined(); // toString() threw an exception.
+ }
+
+ context->js_bindings_->Alert(message);
+ return v8::Undefined();
+ }
+
+ // V8 callback for when "myIpAddress()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> MyIpAddressCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ // We shouldn't be called with any arguments, but will not complain if
+ // we are.
+ std::string result = context->js_bindings_->MyIpAddress();
+ return StdStringToV8String(result);
+ }
+
+ // V8 callback for when "dnsResolve()" is invoked by the PAC script.
+ static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ // We need at least one argument.
+ std::string host;
+ if (args.Length() == 0) {
+ host = "undefined";
+ } else {
+ if (!V8ObjectToString(args[0], &host))
+ return v8::Undefined();
+ }
+
+ std::string result = context->js_bindings_->DnsResolve(host);
+
+ // DoDnsResolve() returns empty string on failure.
+ return result.empty() ? v8::Null() : StdStringToV8String(result);
+ }
+
+ JSBindings* js_bindings_;
+ HostResolver host_resolver_;
+ v8::Persistent<v8::External> v8_this_;
v8::Persistent<v8::Context> v8_context_;
};
@@ -106,7 +266,15 @@ class ProxyResolverV8::Context {
// the |false| argument to ProxyResolver means the ProxyService will handle
// downloading of the PAC script, and notify changes through SetPacScript().
-ProxyResolverV8::ProxyResolverV8() : ProxyResolver(false /*does_fetch*/) {}
+ProxyResolverV8::ProxyResolverV8()
+ : ProxyResolver(false /*does_fetch*/),
+ js_bindings_(new DefaultJSBindings()) {
+}
+
+ProxyResolverV8::ProxyResolverV8(
+ ProxyResolverV8::JSBindings* custom_js_bindings)
+ : ProxyResolver(false), js_bindings_(custom_js_bindings) {
+}
ProxyResolverV8::~ProxyResolverV8() {}
@@ -125,7 +293,7 @@ int ProxyResolverV8::GetProxyForURL(const GURL& query_url,
void ProxyResolverV8::SetPacScript(const std::string& data) {
context_.reset();
if (!data.empty())
- context_.reset(new Context(data));
+ context_.reset(new Context(js_bindings_.get(), data));
}
} // namespace net