summaryrefslogtreecommitdiff
path: root/chromium/net/proxy/proxy_resolver_v8.cc
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/proxy/proxy_resolver_v8.cc
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/net/proxy/proxy_resolver_v8.cc')
-rw-r--r--chromium/net/proxy/proxy_resolver_v8.cc808
1 files changed, 808 insertions, 0 deletions
diff --git a/chromium/net/proxy/proxy_resolver_v8.cc b/chromium/net/proxy/proxy_resolver_v8.cc
new file mode 100644
index 00000000000..87f61028039
--- /dev/null
+++ b/chromium/net/proxy/proxy_resolver_v8.cc
@@ -0,0 +1,808 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/proxy/proxy_resolver_v8.h"
+
+#include <algorithm>
+#include <cstdio>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/proxy/proxy_info.h"
+#include "net/proxy/proxy_resolver_script.h"
+#include "url/gurl.h"
+#include "url/url_canon.h"
+#include "v8/include/v8.h"
+
+// Notes on the javascript environment:
+//
+// For the majority of the PAC utility functions, we use the same code
+// as Firefox. See the javascript library that proxy_resolver_scipt.h
+// pulls in.
+//
+// In addition, we implement a subset of Microsoft's extensions to PAC.
+// - myIpAddressEx()
+// - dnsResolveEx()
+// - isResolvableEx()
+// - isInNetEx()
+// - sortIpAddressList()
+//
+// It is worth noting that the original PAC specification does not describe
+// the return values on failure. Consequently, there are compatibility
+// differences between browsers on what to return on failure, which are
+// illustrated below:
+//
+// --------------------+-------------+-------------------+--------------
+// | Firefox3 | InternetExplorer8 | --> Us <---
+// --------------------+-------------+-------------------+--------------
+// myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
+// dnsResolve() | null | false | null
+// myIpAddressEx() | N/A | "" | ""
+// sortIpAddressList() | N/A | false | false
+// dnsResolveEx() | N/A | "" | ""
+// isInNetEx() | N/A | false | false
+// --------------------+-------------+-------------------+--------------
+//
+// TODO(eroman): The cell above reading ??? means I didn't test it.
+//
+// Another difference is in how dnsResolve() and myIpAddress() are
+// implemented -- whether they should restrict to IPv4 results, or
+// include both IPv4 and IPv6. The following table illustrates the
+// differences:
+//
+// --------------------+-------------+-------------------+--------------
+// | Firefox3 | InternetExplorer8 | --> Us <---
+// --------------------+-------------+-------------------+--------------
+// myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
+// dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
+// isResolvable() | IPv4/IPv6 | IPv4 | IPv4
+// myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
+// isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
+// -----------------+-------------+-------------------+--------------
+
+namespace net {
+
+namespace {
+
+// Pseudo-name for the PAC script.
+const char kPacResourceName[] = "proxy-pac-script.js";
+// Pseudo-name for the PAC utility script.
+const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
+
+// External string wrapper so V8 can access the UTF16 string wrapped by
+// ProxyResolverScriptData.
+class V8ExternalStringFromScriptData
+ : public v8::String::ExternalStringResource {
+ public:
+ explicit V8ExternalStringFromScriptData(
+ const scoped_refptr<ProxyResolverScriptData>& script_data)
+ : script_data_(script_data) {}
+
+ virtual const uint16_t* data() const OVERRIDE {
+ return reinterpret_cast<const uint16*>(script_data_->utf16().data());
+ }
+
+ virtual size_t length() const OVERRIDE {
+ return script_data_->utf16().size();
+ }
+
+ private:
+ const scoped_refptr<ProxyResolverScriptData> script_data_;
+ DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
+};
+
+// External string wrapper so V8 can access a string literal.
+class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
+ public:
+ // |ascii| must be a NULL-terminated C string, and must remain valid
+ // throughout this object's lifetime.
+ V8ExternalASCIILiteral(const char* ascii, size_t length)
+ : ascii_(ascii), length_(length) {
+ DCHECK(IsStringASCII(ascii));
+ }
+
+ virtual const char* data() const OVERRIDE {
+ return ascii_;
+ }
+
+ virtual size_t length() const OVERRIDE {
+ return length_;
+ }
+
+ private:
+ const char* ascii_;
+ size_t length_;
+ DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
+};
+
+// When creating a v8::String from a C++ string we have two choices: create
+// a copy, or create a wrapper that shares the same underlying storage.
+// For small strings it is better to just make a copy, whereas for large
+// strings there are savings by sharing the storage. This number identifies
+// the cutoff length for when to start wrapping rather than creating copies.
+const size_t kMaxStringBytesForCopy = 256;
+
+// Converts a V8 String to a UTF8 std::string.
+std::string V8StringToUTF8(v8::Handle<v8::String> s) {
+ int len = s->Length();
+ std::string result;
+ if (len > 0)
+ s->WriteUtf8(WriteInto(&result, len + 1));
+ return result;
+}
+
+// Converts a V8 String to a UTF16 base::string16.
+base::string16 V8StringToUTF16(v8::Handle<v8::String> s) {
+ int len = s->Length();
+ base::string16 result;
+ // Note that the reinterpret cast is because on Windows string16 is an alias
+ // to wstring, and hence has character type wchar_t not uint16_t.
+ if (len > 0)
+ s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
+ return result;
+}
+
+// Converts an ASCII std::string to a V8 string.
+v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
+ DCHECK(IsStringASCII(s));
+ return v8::String::New(s.data(), s.size());
+}
+
+// Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
+// V8 string.
+v8::Local<v8::String> ScriptDataToV8String(
+ const scoped_refptr<ProxyResolverScriptData>& s) {
+ if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
+ return v8::String::New(
+ reinterpret_cast<const uint16_t*>(s->utf16().data()),
+ s->utf16().size());
+ }
+ return v8::String::NewExternal(new V8ExternalStringFromScriptData(s));
+}
+
+// Converts an ASCII string literal to a V8 string.
+v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
+ DCHECK(IsStringASCII(ascii));
+ size_t length = strlen(ascii);
+ if (length <= kMaxStringBytesForCopy)
+ return v8::String::New(ascii, length);
+ return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
+}
+
+// Stringizes a V8 object by calling its toString() method. Returns true
+// on success. This may fail if the toString() throws an exception.
+bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
+ base::string16* utf16_result) {
+ if (object.IsEmpty())
+ return false;
+
+ v8::HandleScope scope;
+ v8::Local<v8::String> str_object = object->ToString();
+ if (str_object.IsEmpty())
+ return false;
+ *utf16_result = V8StringToUTF16(str_object);
+ return true;
+}
+
+// Extracts an hostname argument from |args|. On success returns true
+// and fills |*hostname| with the result.
+bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
+ std::string* hostname) {
+ // The first argument should be a string.
+ if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
+ return false;
+
+ const base::string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
+
+ // If the hostname is already in ASCII, simply return it as is.
+ if (IsStringASCII(hostname_utf16)) {
+ *hostname = UTF16ToASCII(hostname_utf16);
+ return true;
+ }
+
+ // Otherwise try to convert it from IDN to punycode.
+ const int kInitialBufferSize = 256;
+ url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode_output;
+ if (!url_canon::IDNToASCII(hostname_utf16.data(),
+ hostname_utf16.length(),
+ &punycode_output)) {
+ return false;
+ }
+
+ // |punycode_output| should now be ASCII; convert it to a std::string.
+ // (We could use UTF16ToASCII() instead, but that requires an extra string
+ // copy. Since ASCII is a subset of UTF8 the following is equivalent).
+ bool success = UTF16ToUTF8(punycode_output.data(),
+ punycode_output.length(),
+ hostname);
+ DCHECK(success);
+ DCHECK(IsStringASCII(*hostname));
+ return success;
+}
+
+// Wrapper for passing around IP address strings and IPAddressNumber objects.
+struct IPAddress {
+ IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
+ : string_value(ip_string),
+ ip_address_number(ip_number) {
+ }
+
+ // Used for sorting IP addresses in ascending order in SortIpAddressList().
+ // IP6 addresses are placed ahead of IPv4 addresses.
+ bool operator<(const IPAddress& rhs) const {
+ const IPAddressNumber& ip1 = this->ip_address_number;
+ const IPAddressNumber& ip2 = rhs.ip_address_number;
+ if (ip1.size() != ip2.size())
+ return ip1.size() > ip2.size(); // IPv6 before IPv4.
+ DCHECK(ip1.size() == ip2.size());
+ return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
+ }
+
+ std::string string_value;
+ IPAddressNumber ip_address_number;
+};
+
+// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
+// semi-colon delimited string containing IP addresses.
+// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
+// IP addresses or an empty string if unable to sort the IP address list.
+// Returns 'true' if the sorting was successful, and 'false' if the input was an
+// empty string, a string of separators (";" in this case), or if any of the IP
+// addresses in the input list failed to parse.
+bool SortIpAddressList(const std::string& ip_address_list,
+ std::string* sorted_ip_address_list) {
+ sorted_ip_address_list->clear();
+
+ // Strip all whitespace (mimics IE behavior).
+ std::string cleaned_ip_address_list;
+ RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
+ if (cleaned_ip_address_list.empty())
+ return false;
+
+ // Split-up IP addresses and store them in a vector.
+ std::vector<IPAddress> ip_vector;
+ IPAddressNumber ip_num;
+ base::StringTokenizer str_tok(cleaned_ip_address_list, ";");
+ while (str_tok.GetNext()) {
+ if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
+ return false;
+ ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
+ }
+
+ if (ip_vector.empty()) // Can happen if we have something like
+ return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
+
+ DCHECK(!ip_vector.empty());
+
+ // Sort lists according to ascending numeric value.
+ if (ip_vector.size() > 1)
+ std::stable_sort(ip_vector.begin(), ip_vector.end());
+
+ // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
+ // IPv4).
+ for (size_t i = 0; i < ip_vector.size(); ++i) {
+ if (i > 0)
+ *sorted_ip_address_list += ";";
+ *sorted_ip_address_list += ip_vector[i].string_value;
+ }
+ return true;
+}
+
+// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
+// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
+// slash-delimited IP prefix with the top 'n' bits specified in the bit
+// field. This returns 'true' if the address is in the same subnet, and
+// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
+// format, or if an address and prefix of different types are used (e.g. IPv6
+// address and IPv4 prefix).
+bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
+ IPAddressNumber address;
+ if (!ParseIPLiteralToNumber(ip_address, &address))
+ return false;
+
+ IPAddressNumber prefix;
+ size_t prefix_length_in_bits;
+ if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
+ return false;
+
+ // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
+ if (address.size() != prefix.size())
+ return false;
+
+ DCHECK((address.size() == 4 && prefix.size() == 4) ||
+ (address.size() == 16 && prefix.size() == 16));
+
+ return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
+}
+
+} // namespace
+
+// ProxyResolverV8::Context ---------------------------------------------------
+
+class ProxyResolverV8::Context {
+ public:
+ Context(ProxyResolverV8* parent, v8::Isolate* isolate)
+ : parent_(parent),
+ isolate_(isolate) {
+ DCHECK(isolate);
+ }
+
+ ~Context() {
+ v8::Locker locked(isolate_);
+
+ v8_this_.Dispose(isolate_);
+ v8_context_.Dispose(isolate_);
+ }
+
+ JSBindings* js_bindings() {
+ return parent_->js_bindings_;
+ }
+
+ int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
+ v8::Locker locked(isolate_);
+ v8::HandleScope scope(isolate_);
+
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate_, v8_context_);
+ v8::Context::Scope function_scope(context);
+
+ v8::Local<v8::Value> function;
+ if (!GetFindProxyForURL(&function)) {
+ js_bindings()->OnError(
+ -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ v8::Handle<v8::Value> argv[] = {
+ ASCIIStringToV8String(query_url.spec()),
+ ASCIIStringToV8String(query_url.HostNoBrackets()),
+ };
+
+ v8::TryCatch try_catch;
+ v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
+ context->Global(), arraysize(argv), argv);
+
+ if (try_catch.HasCaught()) {
+ HandleError(try_catch.Message());
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ if (!ret->IsString()) {
+ js_bindings()->OnError(
+ -1, ASCIIToUTF16("FindProxyForURL() did not return a string."));
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ base::string16 ret_str = V8StringToUTF16(ret->ToString());
+
+ if (!IsStringASCII(ret_str)) {
+ // TODO(eroman): Rather than failing when a wide string is returned, we
+ // could extend the parsing to handle IDNA hostnames by
+ // converting them to ASCII punycode.
+ // crbug.com/47234
+ base::string16 error_message =
+ ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
+ "(crbug.com/47234): ") + ret_str;
+ js_bindings()->OnError(-1, error_message);
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ results->UsePacString(UTF16ToASCII(ret_str));
+ return OK;
+ }
+
+ int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
+ v8::Locker locked(isolate_);
+ v8::HandleScope scope(isolate_);
+
+ v8_this_.Reset(isolate_, v8::External::New(this));
+ v8::Local<v8::External> v8_this =
+ v8::Local<v8::External>::New(isolate_, v8_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(ASCIILiteralToV8String("alert"), alert_template);
+
+ v8::Local<v8::FunctionTemplate> my_ip_address_template =
+ v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this);
+ global_template->Set(ASCIILiteralToV8String("myIpAddress"),
+ my_ip_address_template);
+
+ v8::Local<v8::FunctionTemplate> dns_resolve_template =
+ v8::FunctionTemplate::New(&DnsResolveCallback, v8_this);
+ global_template->Set(ASCIILiteralToV8String("dnsResolve"),
+ dns_resolve_template);
+
+ // Microsoft's PAC extensions:
+
+ v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
+ v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this);
+ global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
+ dns_resolve_ex_template);
+
+ v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
+ v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this);
+ global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
+ my_ip_address_ex_template);
+
+ v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
+ v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this);
+ global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
+ sort_ip_address_list_template);
+
+ v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
+ v8::FunctionTemplate::New(&IsInNetExCallback, v8_this);
+ global_template->Set(ASCIILiteralToV8String("isInNetEx"),
+ is_in_net_ex_template);
+
+ v8_context_.Reset(
+ isolate_, v8::Context::New(isolate_, NULL, global_template));
+
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate_, v8_context_);
+ v8::Context::Scope ctx(context);
+
+ // Add the PAC utility functions to the environment.
+ // (This script should never fail, as it is a string literal!)
+ // Note that the two string literals are concatenated.
+ int rv = RunScript(
+ ASCIILiteralToV8String(
+ PROXY_RESOLVER_SCRIPT
+ PROXY_RESOLVER_SCRIPT_EX),
+ kPacUtilityResourceName);
+ if (rv != OK) {
+ NOTREACHED();
+ return rv;
+ }
+
+ // Add the user's PAC code to the environment.
+ rv = RunScript(ScriptDataToV8String(pac_script), kPacResourceName);
+ if (rv != OK)
+ return rv;
+
+ // At a minimum, the FindProxyForURL() function must be defined for this
+ // to be a legitimiate PAC script.
+ v8::Local<v8::Value> function;
+ if (!GetFindProxyForURL(&function)) {
+ js_bindings()->OnError(
+ -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ return OK;
+ }
+
+ void PurgeMemory() {
+ v8::Locker locked(isolate_);
+ v8::V8::LowMemoryNotification();
+ }
+
+ private:
+ bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
+ *function =
+ context->Global()->Get(ASCIILiteralToV8String("FindProxyForURL"));
+ return (*function)->IsFunction();
+ }
+
+ // Handle an exception thrown by V8.
+ void HandleError(v8::Handle<v8::Message> message) {
+ base::string16 error_message;
+ int line_number = -1;
+
+ if (!message.IsEmpty()) {
+ line_number = message->GetLineNumber();
+ V8ObjectToUTF16String(message->Get(), &error_message);
+ }
+
+ js_bindings()->OnError(line_number, error_message);
+ }
+
+ // Compiles and runs |script| in the current V8 context.
+ // Returns OK on success, otherwise an error code.
+ int RunScript(v8::Handle<v8::String> script, const char* script_name) {
+ v8::TryCatch try_catch;
+
+ // Compile the script.
+ v8::ScriptOrigin origin =
+ v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
+ v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
+
+ // Execute.
+ if (!code.IsEmpty())
+ code->Run();
+
+ // Check for errors.
+ if (try_catch.HasCaught()) {
+ HandleError(try_catch.Message());
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ return OK;
+ }
+
+ // V8 callback for when "alert()" is invoked by the PAC script.
+ static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& 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.
+ base::string16 message;
+ if (args.Length() == 0) {
+ message = ASCIIToUTF16("undefined");
+ } else {
+ if (!V8ObjectToUTF16String(args[0], &message))
+ return; // toString() threw an exception.
+ }
+
+ context->js_bindings()->Alert(message);
+ }
+
+ // V8 callback for when "myIpAddress()" is invoked by the PAC script.
+ static void MyIpAddressCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
+ }
+
+ // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
+ static void MyIpAddressExCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
+ }
+
+ // V8 callback for when "dnsResolve()" is invoked by the PAC script.
+ static void DnsResolveCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
+ }
+
+ // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
+ static void DnsResolveExCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
+ }
+
+ // Shared code for implementing:
+ // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
+ static void DnsResolveCallbackHelper(
+ const v8::FunctionCallbackInfo<v8::Value>& args,
+ JSBindings::ResolveDnsOperation op) {
+ Context* context =
+ static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
+
+ std::string hostname;
+
+ // dnsResolve() and dnsResolveEx() need at least 1 argument.
+ if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
+ if (!GetHostnameArgument(args, &hostname)) {
+ if (op == JSBindings::DNS_RESOLVE)
+ args.GetReturnValue().SetNull();
+ return;
+ }
+ }
+
+ std::string result;
+ bool success;
+ bool terminate = false;
+
+ {
+ v8::Unlocker unlocker(args.GetIsolate());
+ success = context->js_bindings()->ResolveDns(
+ hostname, op, &result, &terminate);
+ }
+
+ if (terminate)
+ v8::V8::TerminateExecution(args.GetIsolate());
+
+ if (success) {
+ args.GetReturnValue().Set(ASCIIStringToV8String(result));
+ return;
+ }
+
+ // Each function handles resolution errors differently.
+ switch (op) {
+ case JSBindings::DNS_RESOLVE:
+ args.GetReturnValue().SetNull();
+ return;
+ case JSBindings::DNS_RESOLVE_EX:
+ args.GetReturnValue().SetEmptyString();
+ return;
+ case JSBindings::MY_IP_ADDRESS:
+ args.GetReturnValue().Set(ASCIILiteralToV8String("127.0.0.1"));
+ return;
+ case JSBindings::MY_IP_ADDRESS_EX:
+ args.GetReturnValue().SetEmptyString();
+ return;
+ }
+
+ NOTREACHED();
+ }
+
+ // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
+ static void SortIpAddressListCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // We need at least one string argument.
+ if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
+ args.GetReturnValue().SetNull();
+ return;
+ }
+
+ std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
+ if (!IsStringASCII(ip_address_list)) {
+ args.GetReturnValue().SetNull();
+ return;
+ }
+ std::string sorted_ip_address_list;
+ bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
+ if (!success) {
+ args.GetReturnValue().Set(false);
+ return;
+ }
+ args.GetReturnValue().Set(ASCIIStringToV8String(sorted_ip_address_list));
+ }
+
+ // V8 callback for when "isInNetEx()" is invoked by the PAC script.
+ static void IsInNetExCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // We need at least 2 string arguments.
+ if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
+ args[1].IsEmpty() || !args[1]->IsString()) {
+ args.GetReturnValue().SetNull();
+ return;
+ }
+
+ std::string ip_address = V8StringToUTF8(args[0]->ToString());
+ if (!IsStringASCII(ip_address)) {
+ args.GetReturnValue().Set(false);
+ return;
+ }
+ std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
+ if (!IsStringASCII(ip_prefix)) {
+ args.GetReturnValue().Set(false);
+ return;
+ }
+ args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
+ }
+
+ mutable base::Lock lock_;
+ ProxyResolverV8* parent_;
+ v8::Isolate* isolate_;
+ v8::Persistent<v8::External> v8_this_;
+ v8::Persistent<v8::Context> v8_context_;
+};
+
+// ProxyResolverV8 ------------------------------------------------------------
+
+ProxyResolverV8::ProxyResolverV8()
+ : ProxyResolver(true /*expects_pac_bytes*/),
+ js_bindings_(NULL) {
+}
+
+ProxyResolverV8::~ProxyResolverV8() {}
+
+int ProxyResolverV8::GetProxyForURL(
+ const GURL& query_url, ProxyInfo* results,
+ const CompletionCallback& /*callback*/,
+ RequestHandle* /*request*/,
+ const BoundNetLog& net_log) {
+ DCHECK(js_bindings_);
+
+ // If the V8 instance has not been initialized (either because
+ // SetPacScript() wasn't called yet, or because it failed.
+ if (!context_)
+ return ERR_FAILED;
+
+ // Otherwise call into V8.
+ int rv = context_->ResolveProxy(query_url, results);
+
+ return rv;
+}
+
+void ProxyResolverV8::CancelRequest(RequestHandle request) {
+ // This is a synchronous ProxyResolver; no possibility for async requests.
+ NOTREACHED();
+}
+
+LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
+ NOTREACHED();
+ return LOAD_STATE_IDLE;
+}
+
+void ProxyResolverV8::CancelSetPacScript() {
+ NOTREACHED();
+}
+
+void ProxyResolverV8::PurgeMemory() {
+ context_->PurgeMemory();
+}
+
+int ProxyResolverV8::SetPacScript(
+ const scoped_refptr<ProxyResolverScriptData>& script_data,
+ const CompletionCallback& /*callback*/) {
+ DCHECK(script_data.get());
+ DCHECK(js_bindings_);
+
+ context_.reset();
+ if (script_data->utf16().empty())
+ return ERR_PAC_SCRIPT_FAILED;
+
+ // Try parsing the PAC script.
+ scoped_ptr<Context> context(new Context(this, GetDefaultIsolate()));
+ int rv = context->InitV8(script_data);
+ if (rv == OK)
+ context_.reset(context.release());
+ return rv;
+}
+
+// static
+void ProxyResolverV8::RememberDefaultIsolate() {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ DCHECK(isolate)
+ << "ProxyResolverV8::RememberDefaultIsolate called on wrong thread";
+ DCHECK(g_default_isolate_ == NULL || g_default_isolate_ == isolate)
+ << "Default Isolate can not be changed";
+ g_default_isolate_ = isolate;
+}
+
+#if defined(OS_WIN)
+// static
+void ProxyResolverV8::CreateIsolate() {
+ v8::Isolate* isolate = v8::Isolate::New();
+ DCHECK(isolate);
+ DCHECK(g_default_isolate_ == NULL) << "Default Isolate can not be set twice";
+
+ isolate->Enter();
+ v8::V8::Initialize();
+
+ g_default_isolate_ = isolate;
+}
+#endif // defined(OS_WIN)
+
+// static
+v8::Isolate* ProxyResolverV8::GetDefaultIsolate() {
+ DCHECK(g_default_isolate_)
+ << "Must call ProxyResolverV8::RememberDefaultIsolate() first";
+ return g_default_isolate_;
+}
+
+v8::Isolate* ProxyResolverV8::g_default_isolate_ = NULL;
+
+// static
+size_t ProxyResolverV8::GetTotalHeapSize() {
+ if (!g_default_isolate_)
+ return 0;
+
+ v8::Locker locked(g_default_isolate_);
+ v8::HeapStatistics heap_statistics;
+ g_default_isolate_->GetHeapStatistics(&heap_statistics);
+ return heap_statistics.total_heap_size();
+}
+
+// static
+size_t ProxyResolverV8::GetUsedHeapSize() {
+ if (!g_default_isolate_)
+ return 0;
+
+ v8::Locker locked(g_default_isolate_);
+ v8::HeapStatistics heap_statistics;
+ g_default_isolate_->GetHeapStatistics(&heap_statistics);
+ return heap_statistics.used_heap_size();
+}
+
+} // namespace net