1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/proxy/proxy_resolver_v8.h"
10 #include "base/basictypes.h"
11 #include "base/compiler_specific.h"
12 #include "base/debug/leak_annotations.h"
13 #include "base/logging.h"
14 #include "base/strings/string_tokenizer.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/synchronization/lock.h"
18 #include "gin/public/isolate_holder.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_log.h"
21 #include "net/base/net_util.h"
22 #include "net/proxy/proxy_info.h"
23 #include "net/proxy/proxy_resolver_script.h"
25 #include "url/url_canon.h"
26 #include "v8/include/v8.h"
28 // Notes on the javascript environment:
30 // For the majority of the PAC utility functions, we use the same code
31 // as Firefox. See the javascript library that proxy_resolver_scipt.h
34 // In addition, we implement a subset of Microsoft's extensions to PAC.
39 // - sortIpAddressList()
41 // It is worth noting that the original PAC specification does not describe
42 // the return values on failure. Consequently, there are compatibility
43 // differences between browsers on what to return on failure, which are
46 // --------------------+-------------+-------------------+--------------
47 // | Firefox3 | InternetExplorer8 | --> Us <---
48 // --------------------+-------------+-------------------+--------------
49 // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
50 // dnsResolve() | null | false | null
51 // myIpAddressEx() | N/A | "" | ""
52 // sortIpAddressList() | N/A | false | false
53 // dnsResolveEx() | N/A | "" | ""
54 // isInNetEx() | N/A | false | false
55 // --------------------+-------------+-------------------+--------------
57 // TODO(eroman): The cell above reading ??? means I didn't test it.
59 // Another difference is in how dnsResolve() and myIpAddress() are
60 // implemented -- whether they should restrict to IPv4 results, or
61 // include both IPv4 and IPv6. The following table illustrates the
64 // --------------------+-------------+-------------------+--------------
65 // | Firefox3 | InternetExplorer8 | --> Us <---
66 // --------------------+-------------+-------------------+--------------
67 // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
68 // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
69 // isResolvable() | IPv4/IPv6 | IPv4 | IPv4
70 // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
71 // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
72 // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
73 // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
74 // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
75 // -----------------+-------------+-------------------+--------------
81 // Pseudo-name for the PAC script.
82 const char kPacResourceName
[] = "proxy-pac-script.js";
83 // Pseudo-name for the PAC utility script.
84 const char kPacUtilityResourceName
[] = "proxy-pac-utility-script.js";
86 // External string wrapper so V8 can access the UTF16 string wrapped by
87 // ProxyResolverScriptData.
88 class V8ExternalStringFromScriptData
89 : public v8::String::ExternalStringResource
{
91 explicit V8ExternalStringFromScriptData(
92 const scoped_refptr
<ProxyResolverScriptData
>& script_data
)
93 : script_data_(script_data
) {}
95 virtual const uint16_t* data() const OVERRIDE
{
96 return reinterpret_cast<const uint16
*>(script_data_
->utf16().data());
99 virtual size_t length() const OVERRIDE
{
100 return script_data_
->utf16().size();
104 const scoped_refptr
<ProxyResolverScriptData
> script_data_
;
105 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData
);
108 // External string wrapper so V8 can access a string literal.
109 class V8ExternalASCIILiteral
: public v8::String::ExternalAsciiStringResource
{
111 // |ascii| must be a NULL-terminated C string, and must remain valid
112 // throughout this object's lifetime.
113 V8ExternalASCIILiteral(const char* ascii
, size_t length
)
114 : ascii_(ascii
), length_(length
) {
115 DCHECK(base::IsStringASCII(ascii
));
118 virtual const char* data() const OVERRIDE
{
122 virtual size_t length() const OVERRIDE
{
129 DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral
);
132 // When creating a v8::String from a C++ string we have two choices: create
133 // a copy, or create a wrapper that shares the same underlying storage.
134 // For small strings it is better to just make a copy, whereas for large
135 // strings there are savings by sharing the storage. This number identifies
136 // the cutoff length for when to start wrapping rather than creating copies.
137 const size_t kMaxStringBytesForCopy
= 256;
139 // Converts a V8 String to a UTF8 std::string.
140 std::string
V8StringToUTF8(v8::Handle
<v8::String
> s
) {
141 int len
= s
->Length();
144 s
->WriteUtf8(WriteInto(&result
, len
+ 1));
148 // Converts a V8 String to a UTF16 base::string16.
149 base::string16
V8StringToUTF16(v8::Handle
<v8::String
> s
) {
150 int len
= s
->Length();
151 base::string16 result
;
152 // Note that the reinterpret cast is because on Windows string16 is an alias
153 // to wstring, and hence has character type wchar_t not uint16_t.
155 s
->Write(reinterpret_cast<uint16_t*>(WriteInto(&result
, len
+ 1)), 0, len
);
159 // Converts an ASCII std::string to a V8 string.
160 v8::Local
<v8::String
> ASCIIStringToV8String(v8::Isolate
* isolate
,
161 const std::string
& s
) {
162 DCHECK(base::IsStringASCII(s
));
163 return v8::String::NewFromUtf8(isolate
, s
.data(), v8::String::kNormalString
,
167 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
169 v8::Local
<v8::String
> ScriptDataToV8String(
170 v8::Isolate
* isolate
, const scoped_refptr
<ProxyResolverScriptData
>& s
) {
171 if (s
->utf16().size() * 2 <= kMaxStringBytesForCopy
) {
172 return v8::String::NewFromTwoByte(
174 reinterpret_cast<const uint16_t*>(s
->utf16().data()),
175 v8::String::kNormalString
,
178 return v8::String::NewExternal(isolate
,
179 new V8ExternalStringFromScriptData(s
));
182 // Converts an ASCII string literal to a V8 string.
183 v8::Local
<v8::String
> ASCIILiteralToV8String(v8::Isolate
* isolate
,
185 DCHECK(base::IsStringASCII(ascii
));
186 size_t length
= strlen(ascii
);
187 if (length
<= kMaxStringBytesForCopy
)
188 return v8::String::NewFromUtf8(isolate
, ascii
, v8::String::kNormalString
,
190 return v8::String::NewExternal(isolate
,
191 new V8ExternalASCIILiteral(ascii
, length
));
194 // Stringizes a V8 object by calling its toString() method. Returns true
195 // on success. This may fail if the toString() throws an exception.
196 bool V8ObjectToUTF16String(v8::Handle
<v8::Value
> object
,
197 base::string16
* utf16_result
,
198 v8::Isolate
* isolate
) {
199 if (object
.IsEmpty())
202 v8::HandleScope
scope(isolate
);
203 v8::Local
<v8::String
> str_object
= object
->ToString();
204 if (str_object
.IsEmpty())
206 *utf16_result
= V8StringToUTF16(str_object
);
210 // Extracts an hostname argument from |args|. On success returns true
211 // and fills |*hostname| with the result.
212 bool GetHostnameArgument(const v8::FunctionCallbackInfo
<v8::Value
>& args
,
213 std::string
* hostname
) {
214 // The first argument should be a string.
215 if (args
.Length() == 0 || args
[0].IsEmpty() || !args
[0]->IsString())
218 const base::string16 hostname_utf16
= V8StringToUTF16(args
[0]->ToString());
220 // If the hostname is already in ASCII, simply return it as is.
221 if (base::IsStringASCII(hostname_utf16
)) {
222 *hostname
= base::UTF16ToASCII(hostname_utf16
);
226 // Otherwise try to convert it from IDN to punycode.
227 const int kInitialBufferSize
= 256;
228 url::RawCanonOutputT
<base::char16
, kInitialBufferSize
> punycode_output
;
229 if (!url::IDNToASCII(hostname_utf16
.data(), hostname_utf16
.length(),
234 // |punycode_output| should now be ASCII; convert it to a std::string.
235 // (We could use UTF16ToASCII() instead, but that requires an extra string
236 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
237 bool success
= base::UTF16ToUTF8(punycode_output
.data(),
238 punycode_output
.length(),
241 DCHECK(base::IsStringASCII(*hostname
));
245 // Wrapper for passing around IP address strings and IPAddressNumber objects.
247 IPAddress(const std::string
& ip_string
, const IPAddressNumber
& ip_number
)
248 : string_value(ip_string
),
249 ip_address_number(ip_number
) {
252 // Used for sorting IP addresses in ascending order in SortIpAddressList().
253 // IP6 addresses are placed ahead of IPv4 addresses.
254 bool operator<(const IPAddress
& rhs
) const {
255 const IPAddressNumber
& ip1
= this->ip_address_number
;
256 const IPAddressNumber
& ip2
= rhs
.ip_address_number
;
257 if (ip1
.size() != ip2
.size())
258 return ip1
.size() > ip2
.size(); // IPv6 before IPv4.
259 DCHECK(ip1
.size() == ip2
.size());
260 return memcmp(&ip1
[0], &ip2
[0], ip1
.size()) < 0; // Ascending order.
263 std::string string_value
;
264 IPAddressNumber ip_address_number
;
267 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
268 // semi-colon delimited string containing IP addresses.
269 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
270 // IP addresses or an empty string if unable to sort the IP address list.
271 // Returns 'true' if the sorting was successful, and 'false' if the input was an
272 // empty string, a string of separators (";" in this case), or if any of the IP
273 // addresses in the input list failed to parse.
274 bool SortIpAddressList(const std::string
& ip_address_list
,
275 std::string
* sorted_ip_address_list
) {
276 sorted_ip_address_list
->clear();
278 // Strip all whitespace (mimics IE behavior).
279 std::string cleaned_ip_address_list
;
280 base::RemoveChars(ip_address_list
, " \t", &cleaned_ip_address_list
);
281 if (cleaned_ip_address_list
.empty())
284 // Split-up IP addresses and store them in a vector.
285 std::vector
<IPAddress
> ip_vector
;
286 IPAddressNumber ip_num
;
287 base::StringTokenizer
str_tok(cleaned_ip_address_list
, ";");
288 while (str_tok
.GetNext()) {
289 if (!ParseIPLiteralToNumber(str_tok
.token(), &ip_num
))
291 ip_vector
.push_back(IPAddress(str_tok
.token(), ip_num
));
294 if (ip_vector
.empty()) // Can happen if we have something like
295 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
297 DCHECK(!ip_vector
.empty());
299 // Sort lists according to ascending numeric value.
300 if (ip_vector
.size() > 1)
301 std::stable_sort(ip_vector
.begin(), ip_vector
.end());
303 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
305 for (size_t i
= 0; i
< ip_vector
.size(); ++i
) {
307 *sorted_ip_address_list
+= ";";
308 *sorted_ip_address_list
+= ip_vector
[i
].string_value
;
313 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
314 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
315 // slash-delimited IP prefix with the top 'n' bits specified in the bit
316 // field. This returns 'true' if the address is in the same subnet, and
317 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
318 // format, or if an address and prefix of different types are used (e.g. IPv6
319 // address and IPv4 prefix).
320 bool IsInNetEx(const std::string
& ip_address
, const std::string
& ip_prefix
) {
321 IPAddressNumber address
;
322 if (!ParseIPLiteralToNumber(ip_address
, &address
))
325 IPAddressNumber prefix
;
326 size_t prefix_length_in_bits
;
327 if (!ParseCIDRBlock(ip_prefix
, &prefix
, &prefix_length_in_bits
))
330 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
331 if (address
.size() != prefix
.size())
334 DCHECK((address
.size() == 4 && prefix
.size() == 4) ||
335 (address
.size() == 16 && prefix
.size() == 16));
337 return IPNumberMatchesPrefix(address
, prefix
, prefix_length_in_bits
);
342 // ProxyResolverV8::Context ---------------------------------------------------
344 class ProxyResolverV8::Context
{
346 Context(ProxyResolverV8
* parent
, v8::Isolate
* isolate
)
353 v8::Locker
locked(isolate_
);
354 v8::Isolate::Scope
isolate_scope(isolate_
);
360 JSBindings
* js_bindings() {
361 return parent_
->js_bindings_
;
364 int ResolveProxy(const GURL
& query_url
, ProxyInfo
* results
) {
365 v8::Locker
locked(isolate_
);
366 v8::Isolate::Scope
isolate_scope(isolate_
);
367 v8::HandleScope
scope(isolate_
);
369 v8::Local
<v8::Context
> context
=
370 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
371 v8::Context::Scope
function_scope(context
);
373 v8::Local
<v8::Value
> function
;
374 if (!GetFindProxyForURL(&function
)) {
375 js_bindings()->OnError(
376 -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
377 return ERR_PAC_SCRIPT_FAILED
;
380 v8::Handle
<v8::Value
> argv
[] = {
381 ASCIIStringToV8String(isolate_
, query_url
.spec()),
382 ASCIIStringToV8String(isolate_
, query_url
.HostNoBrackets()),
385 v8::TryCatch try_catch
;
386 v8::Local
<v8::Value
> ret
= v8::Function::Cast(*function
)->Call(
387 context
->Global(), arraysize(argv
), argv
);
389 if (try_catch
.HasCaught()) {
390 HandleError(try_catch
.Message());
391 return ERR_PAC_SCRIPT_FAILED
;
394 if (!ret
->IsString()) {
395 js_bindings()->OnError(
396 -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
397 return ERR_PAC_SCRIPT_FAILED
;
400 base::string16 ret_str
= V8StringToUTF16(ret
->ToString());
402 if (!base::IsStringASCII(ret_str
)) {
403 // TODO(eroman): Rather than failing when a wide string is returned, we
404 // could extend the parsing to handle IDNA hostnames by
405 // converting them to ASCII punycode.
407 base::string16 error_message
=
408 base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
409 "(crbug.com/47234): ") + ret_str
;
410 js_bindings()->OnError(-1, error_message
);
411 return ERR_PAC_SCRIPT_FAILED
;
414 results
->UsePacString(base::UTF16ToASCII(ret_str
));
418 int InitV8(const scoped_refptr
<ProxyResolverScriptData
>& pac_script
) {
419 v8::Locker
locked(isolate_
);
420 v8::Isolate::Scope
isolate_scope(isolate_
);
421 v8::HandleScope
scope(isolate_
);
423 v8_this_
.Reset(isolate_
, v8::External::New(isolate_
, this));
424 v8::Local
<v8::External
> v8_this
=
425 v8::Local
<v8::External
>::New(isolate_
, v8_this_
);
426 v8::Local
<v8::ObjectTemplate
> global_template
=
427 v8::ObjectTemplate::New(isolate_
);
429 // Attach the javascript bindings.
430 v8::Local
<v8::FunctionTemplate
> alert_template
=
431 v8::FunctionTemplate::New(isolate_
, &AlertCallback
, v8_this
);
432 global_template
->Set(ASCIILiteralToV8String(isolate_
, "alert"),
435 v8::Local
<v8::FunctionTemplate
> my_ip_address_template
=
436 v8::FunctionTemplate::New(isolate_
, &MyIpAddressCallback
, v8_this
);
437 global_template
->Set(ASCIILiteralToV8String(isolate_
, "myIpAddress"),
438 my_ip_address_template
);
440 v8::Local
<v8::FunctionTemplate
> dns_resolve_template
=
441 v8::FunctionTemplate::New(isolate_
, &DnsResolveCallback
, v8_this
);
442 global_template
->Set(ASCIILiteralToV8String(isolate_
, "dnsResolve"),
443 dns_resolve_template
);
445 // Microsoft's PAC extensions:
447 v8::Local
<v8::FunctionTemplate
> dns_resolve_ex_template
=
448 v8::FunctionTemplate::New(isolate_
, &DnsResolveExCallback
, v8_this
);
449 global_template
->Set(ASCIILiteralToV8String(isolate_
, "dnsResolveEx"),
450 dns_resolve_ex_template
);
452 v8::Local
<v8::FunctionTemplate
> my_ip_address_ex_template
=
453 v8::FunctionTemplate::New(isolate_
, &MyIpAddressExCallback
, v8_this
);
454 global_template
->Set(ASCIILiteralToV8String(isolate_
, "myIpAddressEx"),
455 my_ip_address_ex_template
);
457 v8::Local
<v8::FunctionTemplate
> sort_ip_address_list_template
=
458 v8::FunctionTemplate::New(isolate_
,
459 &SortIpAddressListCallback
,
461 global_template
->Set(ASCIILiteralToV8String(isolate_
, "sortIpAddressList"),
462 sort_ip_address_list_template
);
464 v8::Local
<v8::FunctionTemplate
> is_in_net_ex_template
=
465 v8::FunctionTemplate::New(isolate_
, &IsInNetExCallback
, v8_this
);
466 global_template
->Set(ASCIILiteralToV8String(isolate_
, "isInNetEx"),
467 is_in_net_ex_template
);
470 isolate_
, v8::Context::New(isolate_
, NULL
, global_template
));
472 v8::Local
<v8::Context
> context
=
473 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
474 v8::Context::Scope
ctx(context
);
476 // Add the PAC utility functions to the environment.
477 // (This script should never fail, as it is a string literal!)
478 // Note that the two string literals are concatenated.
480 ASCIILiteralToV8String(
482 PROXY_RESOLVER_SCRIPT
483 PROXY_RESOLVER_SCRIPT_EX
),
484 kPacUtilityResourceName
);
490 // Add the user's PAC code to the environment.
492 RunScript(ScriptDataToV8String(isolate_
, pac_script
), kPacResourceName
);
496 // At a minimum, the FindProxyForURL() function must be defined for this
497 // to be a legitimiate PAC script.
498 v8::Local
<v8::Value
> function
;
499 if (!GetFindProxyForURL(&function
)) {
500 js_bindings()->OnError(
501 -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
502 return ERR_PAC_SCRIPT_FAILED
;
509 bool GetFindProxyForURL(v8::Local
<v8::Value
>* function
) {
510 v8::Local
<v8::Context
> context
=
511 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
513 context
->Global()->Get(
514 ASCIILiteralToV8String(isolate_
, "FindProxyForURL"));
515 return (*function
)->IsFunction();
518 // Handle an exception thrown by V8.
519 void HandleError(v8::Handle
<v8::Message
> message
) {
520 base::string16 error_message
;
521 int line_number
= -1;
523 if (!message
.IsEmpty()) {
524 line_number
= message
->GetLineNumber();
525 V8ObjectToUTF16String(message
->Get(), &error_message
, isolate_
);
528 js_bindings()->OnError(line_number
, error_message
);
531 // Compiles and runs |script| in the current V8 context.
532 // Returns OK on success, otherwise an error code.
533 int RunScript(v8::Handle
<v8::String
> script
, const char* script_name
) {
534 v8::TryCatch try_catch
;
536 // Compile the script.
537 v8::ScriptOrigin origin
=
538 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_
, script_name
));
539 v8::Local
<v8::Script
> code
= v8::Script::Compile(script
, &origin
);
546 if (try_catch
.HasCaught()) {
547 HandleError(try_catch
.Message());
548 return ERR_PAC_SCRIPT_FAILED
;
554 // V8 callback for when "alert()" is invoked by the PAC script.
555 static void AlertCallback(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
557 static_cast<Context
*>(v8::External::Cast(*args
.Data())->Value());
559 // Like firefox we assume "undefined" if no argument was specified, and
560 // disregard any arguments beyond the first.
561 base::string16 message
;
562 if (args
.Length() == 0) {
563 message
= base::ASCIIToUTF16("undefined");
565 if (!V8ObjectToUTF16String(args
[0], &message
, args
.GetIsolate()))
566 return; // toString() threw an exception.
569 context
->js_bindings()->Alert(message
);
572 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
573 static void MyIpAddressCallback(
574 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
575 DnsResolveCallbackHelper(args
, JSBindings::MY_IP_ADDRESS
);
578 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
579 static void MyIpAddressExCallback(
580 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
581 DnsResolveCallbackHelper(args
, JSBindings::MY_IP_ADDRESS_EX
);
584 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
585 static void DnsResolveCallback(
586 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
587 DnsResolveCallbackHelper(args
, JSBindings::DNS_RESOLVE
);
590 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
591 static void DnsResolveExCallback(
592 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
593 DnsResolveCallbackHelper(args
, JSBindings::DNS_RESOLVE_EX
);
596 // Shared code for implementing:
597 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
598 static void DnsResolveCallbackHelper(
599 const v8::FunctionCallbackInfo
<v8::Value
>& args
,
600 JSBindings::ResolveDnsOperation op
) {
602 static_cast<Context
*>(v8::External::Cast(*args
.Data())->Value());
604 std::string hostname
;
606 // dnsResolve() and dnsResolveEx() need at least 1 argument.
607 if (op
== JSBindings::DNS_RESOLVE
|| op
== JSBindings::DNS_RESOLVE_EX
) {
608 if (!GetHostnameArgument(args
, &hostname
)) {
609 if (op
== JSBindings::DNS_RESOLVE
)
610 args
.GetReturnValue().SetNull();
617 bool terminate
= false;
620 v8::Unlocker
unlocker(args
.GetIsolate());
621 success
= context
->js_bindings()->ResolveDns(
622 hostname
, op
, &result
, &terminate
);
626 v8::V8::TerminateExecution(args
.GetIsolate());
629 args
.GetReturnValue().Set(
630 ASCIIStringToV8String(args
.GetIsolate(), result
));
634 // Each function handles resolution errors differently.
636 case JSBindings::DNS_RESOLVE
:
637 args
.GetReturnValue().SetNull();
639 case JSBindings::DNS_RESOLVE_EX
:
640 args
.GetReturnValue().SetEmptyString();
642 case JSBindings::MY_IP_ADDRESS
:
643 args
.GetReturnValue().Set(
644 ASCIILiteralToV8String(args
.GetIsolate(), "127.0.0.1"));
646 case JSBindings::MY_IP_ADDRESS_EX
:
647 args
.GetReturnValue().SetEmptyString();
654 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
655 static void SortIpAddressListCallback(
656 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
657 // We need at least one string argument.
658 if (args
.Length() == 0 || args
[0].IsEmpty() || !args
[0]->IsString()) {
659 args
.GetReturnValue().SetNull();
663 std::string ip_address_list
= V8StringToUTF8(args
[0]->ToString());
664 if (!base::IsStringASCII(ip_address_list
)) {
665 args
.GetReturnValue().SetNull();
668 std::string sorted_ip_address_list
;
669 bool success
= SortIpAddressList(ip_address_list
, &sorted_ip_address_list
);
671 args
.GetReturnValue().Set(false);
674 args
.GetReturnValue().Set(
675 ASCIIStringToV8String(args
.GetIsolate(), sorted_ip_address_list
));
678 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
679 static void IsInNetExCallback(
680 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
681 // We need at least 2 string arguments.
682 if (args
.Length() < 2 || args
[0].IsEmpty() || !args
[0]->IsString() ||
683 args
[1].IsEmpty() || !args
[1]->IsString()) {
684 args
.GetReturnValue().SetNull();
688 std::string ip_address
= V8StringToUTF8(args
[0]->ToString());
689 if (!base::IsStringASCII(ip_address
)) {
690 args
.GetReturnValue().Set(false);
693 std::string ip_prefix
= V8StringToUTF8(args
[1]->ToString());
694 if (!base::IsStringASCII(ip_prefix
)) {
695 args
.GetReturnValue().Set(false);
698 args
.GetReturnValue().Set(IsInNetEx(ip_address
, ip_prefix
));
701 mutable base::Lock lock_
;
702 ProxyResolverV8
* parent_
;
703 v8::Isolate
* isolate_
;
704 v8::Persistent
<v8::External
> v8_this_
;
705 v8::Persistent
<v8::Context
> v8_context_
;
708 // ProxyResolverV8 ------------------------------------------------------------
710 ProxyResolverV8::ProxyResolverV8()
711 : ProxyResolver(true /*expects_pac_bytes*/),
715 ProxyResolverV8::~ProxyResolverV8() {}
717 int ProxyResolverV8::GetProxyForURL(
718 const GURL
& query_url
, ProxyInfo
* results
,
719 const CompletionCallback
& /*callback*/,
720 RequestHandle
* /*request*/,
721 const BoundNetLog
& net_log
) {
722 DCHECK(js_bindings_
);
724 // If the V8 instance has not been initialized (either because
725 // SetPacScript() wasn't called yet, or because it failed.
729 // Otherwise call into V8.
730 int rv
= context_
->ResolveProxy(query_url
, results
);
735 void ProxyResolverV8::CancelRequest(RequestHandle request
) {
736 // This is a synchronous ProxyResolver; no possibility for async requests.
740 LoadState
ProxyResolverV8::GetLoadState(RequestHandle request
) const {
742 return LOAD_STATE_IDLE
;
745 void ProxyResolverV8::CancelSetPacScript() {
749 int ProxyResolverV8::SetPacScript(
750 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
751 const CompletionCallback
& /*callback*/) {
752 DCHECK(script_data
.get());
753 DCHECK(js_bindings_
);
756 if (script_data
->utf16().empty())
757 return ERR_PAC_SCRIPT_FAILED
;
759 // Try parsing the PAC script.
760 scoped_ptr
<Context
> context(new Context(this, GetDefaultIsolate()));
761 int rv
= context
->InitV8(script_data
);
763 context_
.reset(context
.release());
768 void ProxyResolverV8::EnsureIsolateCreated() {
769 if (g_proxy_resolver_isolate_
)
771 g_proxy_resolver_isolate_
=
772 new gin::IsolateHolder(gin::IsolateHolder::kNonStrictMode
);
773 ANNOTATE_LEAKING_OBJECT_PTR(g_proxy_resolver_isolate_
);
777 v8::Isolate
* ProxyResolverV8::GetDefaultIsolate() {
778 DCHECK(g_proxy_resolver_isolate_
)
779 << "Must call ProxyResolverV8::EnsureIsolateCreated() first";
780 return g_proxy_resolver_isolate_
->isolate();
783 gin::IsolateHolder
* ProxyResolverV8::g_proxy_resolver_isolate_
= NULL
;
786 size_t ProxyResolverV8::GetTotalHeapSize() {
787 if (!g_proxy_resolver_isolate_
)
790 v8::Locker
locked(g_proxy_resolver_isolate_
->isolate());
791 v8::Isolate::Scope
isolate_scope(g_proxy_resolver_isolate_
->isolate());
792 v8::HeapStatistics heap_statistics
;
793 g_proxy_resolver_isolate_
->isolate()->GetHeapStatistics(&heap_statistics
);
794 return heap_statistics
.total_heap_size();
798 size_t ProxyResolverV8::GetUsedHeapSize() {
799 if (!g_proxy_resolver_isolate_
)
802 v8::Locker
locked(g_proxy_resolver_isolate_
->isolate());
803 v8::Isolate::Scope
isolate_scope(g_proxy_resolver_isolate_
->isolate());
804 v8::HeapStatistics heap_statistics
;
805 g_proxy_resolver_isolate_
->isolate()->GetHeapStatistics(&heap_statistics
);
806 return heap_statistics
.used_heap_size();