diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/proxy/proxy_resolver_v8.cc | |
download | qtwebengine-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.cc | 808 |
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 |