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/auto_reset.h"
11 #include "base/basictypes.h"
12 #include "base/compiler_specific.h"
13 #include "base/debug/leak_annotations.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h"
16 #include "base/strings/string_tokenizer.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/synchronization/lock.h"
20 #include "gin/array_buffer.h"
21 #include "gin/public/isolate_holder.h"
22 #include "gin/v8_initializer.h"
23 #include "net/base/ip_address_number.h"
24 #include "net/base/net_errors.h"
25 #include "net/proxy/proxy_info.h"
26 #include "net/proxy/proxy_resolver_script.h"
27 #include "net/proxy/proxy_resolver_script_data.h"
29 #include "url/url_canon.h"
30 #include "v8/include/v8.h"
32 // Notes on the javascript environment:
34 // For the majority of the PAC utility functions, we use the same code
35 // as Firefox. See the javascript library that proxy_resolver_scipt.h
38 // In addition, we implement a subset of Microsoft's extensions to PAC.
43 // - sortIpAddressList()
45 // It is worth noting that the original PAC specification does not describe
46 // the return values on failure. Consequently, there are compatibility
47 // differences between browsers on what to return on failure, which are
50 // --------------------+-------------+-------------------+--------------
51 // | Firefox3 | InternetExplorer8 | --> Us <---
52 // --------------------+-------------+-------------------+--------------
53 // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
54 // dnsResolve() | null | false | null
55 // myIpAddressEx() | N/A | "" | ""
56 // sortIpAddressList() | N/A | false | false
57 // dnsResolveEx() | N/A | "" | ""
58 // isInNetEx() | N/A | false | false
59 // --------------------+-------------+-------------------+--------------
61 // TODO(eroman): The cell above reading ??? means I didn't test it.
63 // Another difference is in how dnsResolve() and myIpAddress() are
64 // implemented -- whether they should restrict to IPv4 results, or
65 // include both IPv4 and IPv6. The following table illustrates the
68 // --------------------+-------------+-------------------+--------------
69 // | Firefox3 | InternetExplorer8 | --> Us <---
70 // --------------------+-------------+-------------------+--------------
71 // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
72 // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
73 // isResolvable() | IPv4/IPv6 | IPv4 | IPv4
74 // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
75 // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
76 // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
77 // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
78 // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
79 // -----------------+-------------+-------------------+--------------
85 // Pseudo-name for the PAC script.
86 const char kPacResourceName
[] = "proxy-pac-script.js";
87 // Pseudo-name for the PAC utility script.
88 const char kPacUtilityResourceName
[] = "proxy-pac-utility-script.js";
90 // External string wrapper so V8 can access the UTF16 string wrapped by
91 // ProxyResolverScriptData.
92 class V8ExternalStringFromScriptData
93 : public v8::String::ExternalStringResource
{
95 explicit V8ExternalStringFromScriptData(
96 const scoped_refptr
<ProxyResolverScriptData
>& script_data
)
97 : script_data_(script_data
) {}
99 const uint16_t* data() const override
{
100 return reinterpret_cast<const uint16
*>(script_data_
->utf16().data());
103 size_t length() const override
{ return script_data_
->utf16().size(); }
106 const scoped_refptr
<ProxyResolverScriptData
> script_data_
;
107 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData
);
110 // External string wrapper so V8 can access a string literal.
111 class V8ExternalASCIILiteral
112 : public v8::String::ExternalOneByteStringResource
{
114 // |ascii| must be a NULL-terminated C string, and must remain valid
115 // throughout this object's lifetime.
116 V8ExternalASCIILiteral(const char* ascii
, size_t length
)
117 : ascii_(ascii
), length_(length
) {
118 DCHECK(base::IsStringASCII(ascii
));
121 const char* data() const override
{ return ascii_
; }
123 size_t length() const override
{ return length_
; }
128 DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral
);
131 // When creating a v8::String from a C++ string we have two choices: create
132 // a copy, or create a wrapper that shares the same underlying storage.
133 // For small strings it is better to just make a copy, whereas for large
134 // strings there are savings by sharing the storage. This number identifies
135 // the cutoff length for when to start wrapping rather than creating copies.
136 const size_t kMaxStringBytesForCopy
= 256;
138 // Converts a V8 String to a UTF8 std::string.
139 std::string
V8StringToUTF8(v8::Local
<v8::String
> s
) {
140 int len
= s
->Length();
143 s
->WriteUtf8(WriteInto(&result
, len
+ 1));
147 // Converts a V8 String to a UTF16 base::string16.
148 base::string16
V8StringToUTF16(v8::Local
<v8::String
> s
) {
149 int len
= s
->Length();
150 base::string16 result
;
151 // Note that the reinterpret cast is because on Windows string16 is an alias
152 // to wstring, and hence has character type wchar_t not uint16_t.
154 s
->Write(reinterpret_cast<uint16_t*>(WriteInto(&result
, len
+ 1)), 0, len
);
158 // Converts an ASCII std::string to a V8 string.
159 v8::Local
<v8::String
> ASCIIStringToV8String(v8::Isolate
* isolate
,
160 const std::string
& s
) {
161 DCHECK(base::IsStringASCII(s
));
162 return v8::String::NewFromUtf8(isolate
, s
.data(), v8::NewStringType::kNormal
,
163 s
.size()).ToLocalChecked();
166 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
168 v8::Local
<v8::String
> ScriptDataToV8String(
169 v8::Isolate
* isolate
, const scoped_refptr
<ProxyResolverScriptData
>& s
) {
170 if (s
->utf16().size() * 2 <= kMaxStringBytesForCopy
) {
171 return v8::String::NewFromTwoByte(
172 isolate
, reinterpret_cast<const uint16_t*>(s
->utf16().data()),
173 v8::NewStringType::kNormal
, s
->utf16().size()).ToLocalChecked();
175 return v8::String::NewExternalTwoByte(
176 isolate
, new V8ExternalStringFromScriptData(s
)).ToLocalChecked();
179 // Converts an ASCII string literal to a V8 string.
180 v8::Local
<v8::String
> ASCIILiteralToV8String(v8::Isolate
* isolate
,
182 DCHECK(base::IsStringASCII(ascii
));
183 size_t length
= strlen(ascii
);
184 if (length
<= kMaxStringBytesForCopy
)
185 return v8::String::NewFromUtf8(isolate
, ascii
, v8::NewStringType::kNormal
,
186 length
).ToLocalChecked();
187 return v8::String::NewExternalOneByte(
188 isolate
, new V8ExternalASCIILiteral(ascii
, length
))
192 // Stringizes a V8 object by calling its toString() method. Returns true
193 // on success. This may fail if the toString() throws an exception.
194 bool V8ObjectToUTF16String(v8::Local
<v8::Value
> object
,
195 base::string16
* utf16_result
,
196 v8::Isolate
* isolate
) {
197 if (object
.IsEmpty())
200 v8::HandleScope
scope(isolate
);
201 v8::Local
<v8::String
> str_object
;
202 if (!object
->ToString(isolate
->GetCurrentContext()).ToLocal(&str_object
))
204 *utf16_result
= V8StringToUTF16(str_object
);
208 // Extracts an hostname argument from |args|. On success returns true
209 // and fills |*hostname| with the result.
210 bool GetHostnameArgument(const v8::FunctionCallbackInfo
<v8::Value
>& args
,
211 std::string
* hostname
) {
212 // The first argument should be a string.
213 if (args
.Length() == 0 || args
[0].IsEmpty() || !args
[0]->IsString())
216 const base::string16 hostname_utf16
=
217 V8StringToUTF16(v8::Local
<v8::String
>::Cast(args
[0]));
219 // If the hostname is already in ASCII, simply return it as is.
220 if (base::IsStringASCII(hostname_utf16
)) {
221 *hostname
= base::UTF16ToASCII(hostname_utf16
);
225 // Otherwise try to convert it from IDN to punycode.
226 const int kInitialBufferSize
= 256;
227 url::RawCanonOutputT
<base::char16
, kInitialBufferSize
> punycode_output
;
228 if (!url::IDNToASCII(hostname_utf16
.data(), hostname_utf16
.length(),
233 // |punycode_output| should now be ASCII; convert it to a std::string.
234 // (We could use UTF16ToASCII() instead, but that requires an extra string
235 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
236 bool success
= base::UTF16ToUTF8(punycode_output
.data(),
237 punycode_output
.length(),
240 DCHECK(base::IsStringASCII(*hostname
));
244 // Wrapper for passing around IP address strings and IPAddressNumber objects.
246 IPAddress(const std::string
& ip_string
, const IPAddressNumber
& ip_number
)
247 : string_value(ip_string
),
248 ip_address_number(ip_number
) {
251 // Used for sorting IP addresses in ascending order in SortIpAddressList().
252 // IP6 addresses are placed ahead of IPv4 addresses.
253 bool operator<(const IPAddress
& rhs
) const {
254 const IPAddressNumber
& ip1
= this->ip_address_number
;
255 const IPAddressNumber
& ip2
= rhs
.ip_address_number
;
256 if (ip1
.size() != ip2
.size())
257 return ip1
.size() > ip2
.size(); // IPv6 before IPv4.
258 DCHECK(ip1
.size() == ip2
.size());
259 return memcmp(&ip1
[0], &ip2
[0], ip1
.size()) < 0; // Ascending order.
262 std::string string_value
;
263 IPAddressNumber ip_address_number
;
266 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
267 // semi-colon delimited string containing IP addresses.
268 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
269 // IP addresses or an empty string if unable to sort the IP address list.
270 // Returns 'true' if the sorting was successful, and 'false' if the input was an
271 // empty string, a string of separators (";" in this case), or if any of the IP
272 // addresses in the input list failed to parse.
273 bool SortIpAddressList(const std::string
& ip_address_list
,
274 std::string
* sorted_ip_address_list
) {
275 sorted_ip_address_list
->clear();
277 // Strip all whitespace (mimics IE behavior).
278 std::string cleaned_ip_address_list
;
279 base::RemoveChars(ip_address_list
, " \t", &cleaned_ip_address_list
);
280 if (cleaned_ip_address_list
.empty())
283 // Split-up IP addresses and store them in a vector.
284 std::vector
<IPAddress
> ip_vector
;
285 IPAddressNumber ip_num
;
286 base::StringTokenizer
str_tok(cleaned_ip_address_list
, ";");
287 while (str_tok
.GetNext()) {
288 if (!ParseIPLiteralToNumber(str_tok
.token(), &ip_num
))
290 ip_vector
.push_back(IPAddress(str_tok
.token(), ip_num
));
293 if (ip_vector
.empty()) // Can happen if we have something like
294 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
296 DCHECK(!ip_vector
.empty());
298 // Sort lists according to ascending numeric value.
299 if (ip_vector
.size() > 1)
300 std::stable_sort(ip_vector
.begin(), ip_vector
.end());
302 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
304 for (size_t i
= 0; i
< ip_vector
.size(); ++i
) {
306 *sorted_ip_address_list
+= ";";
307 *sorted_ip_address_list
+= ip_vector
[i
].string_value
;
312 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
313 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
314 // slash-delimited IP prefix with the top 'n' bits specified in the bit
315 // field. This returns 'true' if the address is in the same subnet, and
316 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
317 // format, or if an address and prefix of different types are used (e.g. IPv6
318 // address and IPv4 prefix).
319 bool IsInNetEx(const std::string
& ip_address
, const std::string
& ip_prefix
) {
320 IPAddressNumber address
;
321 if (!ParseIPLiteralToNumber(ip_address
, &address
))
324 IPAddressNumber prefix
;
325 size_t prefix_length_in_bits
;
326 if (!ParseCIDRBlock(ip_prefix
, &prefix
, &prefix_length_in_bits
))
329 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
330 if (address
.size() != prefix
.size())
333 DCHECK((address
.size() == 4 && prefix
.size() == 4) ||
334 (address
.size() == 16 && prefix
.size() == 16));
336 return IPNumberMatchesPrefix(address
, prefix
, prefix_length_in_bits
);
339 // Consider only single component domains like 'foo' as plain host names.
340 bool IsPlainHostName(const std::string
& hostname_utf8
) {
341 if (hostname_utf8
.find('.') != std::string::npos
)
344 // IPv6 literals might not contain any periods, however are not considered
346 IPAddressNumber unused
;
347 return !ParseIPLiteralToNumber(hostname_utf8
, &unused
);
350 // All instances of ProxyResolverV8 share the same v8::Isolate. This isolate is
351 // created lazily the first time it is needed and lives until process shutdown.
352 // This creation might happen from any thread, as ProxyResolverV8 is typically
353 // run in a threadpool.
355 // TODO(eroman): The lazily created isolate is never freed. Instead it should be
356 // disposed once there are no longer any ProxyResolverV8 referencing it.
357 class SharedIsolateFactory
{
359 SharedIsolateFactory() : has_initialized_v8_(false) {}
361 // Lazily creates a v8::Isolate, or returns the already created instance.
362 v8::Isolate
* GetSharedIsolate() {
363 base::AutoLock
l(lock_
);
366 // Do one-time initialization for V8.
367 if (!has_initialized_v8_
) {
368 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
369 gin::V8Initializer::LoadV8Snapshot();
370 gin::V8Initializer::LoadV8Natives();
373 gin::IsolateHolder::Initialize(
374 gin::IsolateHolder::kNonStrictMode
,
375 gin::ArrayBufferAllocator::SharedInstance());
377 has_initialized_v8_
= true;
380 holder_
.reset(new gin::IsolateHolder(gin::IsolateHolder::kUseLocker
));
383 return holder_
->isolate();
386 v8::Isolate
* GetSharedIsolateWithoutCreating() {
387 base::AutoLock
l(lock_
);
388 return holder_
? holder_
->isolate() : NULL
;
393 scoped_ptr
<gin::IsolateHolder
> holder_
;
394 bool has_initialized_v8_
;
396 DISALLOW_COPY_AND_ASSIGN(SharedIsolateFactory
);
399 base::LazyInstance
<SharedIsolateFactory
>::Leaky g_isolate_factory
=
400 LAZY_INSTANCE_INITIALIZER
;
404 // ProxyResolverV8::Context ---------------------------------------------------
406 class ProxyResolverV8::Context
{
408 explicit Context(v8::Isolate
* isolate
)
409 : js_bindings_(nullptr), isolate_(isolate
) {
414 v8::Locker
locked(isolate_
);
415 v8::Isolate::Scope
isolate_scope(isolate_
);
421 JSBindings
* js_bindings() { return js_bindings_
; }
423 int ResolveProxy(const GURL
& query_url
,
425 JSBindings
* bindings
) {
427 base::AutoReset
<JSBindings
*> bindings_reset(&js_bindings_
, bindings
);
428 v8::Locker
locked(isolate_
);
429 v8::Isolate::Scope
isolate_scope(isolate_
);
430 v8::HandleScope
scope(isolate_
);
432 v8::Local
<v8::Context
> context
=
433 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
434 v8::Context::Scope
function_scope(context
);
436 v8::Local
<v8::Value
> function
;
437 int rv
= GetFindProxyForURL(&function
);
441 v8::Local
<v8::Value
> argv
[] = {
442 ASCIIStringToV8String(isolate_
, query_url
.spec()),
443 ASCIIStringToV8String(isolate_
, query_url
.HostNoBrackets()),
446 v8::TryCatch
try_catch(isolate_
);
447 v8::Local
<v8::Value
> ret
;
448 if (!v8::Function::Cast(*function
)
449 ->Call(context
, context
->Global(), arraysize(argv
), argv
)
451 DCHECK(try_catch
.HasCaught());
452 HandleError(try_catch
.Message());
453 return ERR_PAC_SCRIPT_FAILED
;
456 if (!ret
->IsString()) {
457 js_bindings()->OnError(
458 -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
459 return ERR_PAC_SCRIPT_FAILED
;
462 base::string16 ret_str
= V8StringToUTF16(v8::Local
<v8::String
>::Cast(ret
));
464 if (!base::IsStringASCII(ret_str
)) {
465 // TODO(eroman): Rather than failing when a wide string is returned, we
466 // could extend the parsing to handle IDNA hostnames by
467 // converting them to ASCII punycode.
469 base::string16 error_message
=
470 base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
471 "(crbug.com/47234): ") + ret_str
;
472 js_bindings()->OnError(-1, error_message
);
473 return ERR_PAC_SCRIPT_FAILED
;
476 results
->UsePacString(base::UTF16ToASCII(ret_str
));
480 int InitV8(const scoped_refptr
<ProxyResolverScriptData
>& pac_script
,
481 JSBindings
* bindings
) {
482 base::AutoReset
<JSBindings
*> bindings_reset(&js_bindings_
, bindings
);
483 v8::Locker
locked(isolate_
);
484 v8::Isolate::Scope
isolate_scope(isolate_
);
485 v8::HandleScope
scope(isolate_
);
487 v8_this_
.Reset(isolate_
, v8::External::New(isolate_
, this));
488 v8::Local
<v8::External
> v8_this
=
489 v8::Local
<v8::External
>::New(isolate_
, v8_this_
);
490 v8::Local
<v8::ObjectTemplate
> global_template
=
491 v8::ObjectTemplate::New(isolate_
);
493 // Attach the javascript bindings.
494 v8::Local
<v8::FunctionTemplate
> alert_template
=
495 v8::FunctionTemplate::New(isolate_
, &AlertCallback
, v8_this
);
496 global_template
->Set(ASCIILiteralToV8String(isolate_
, "alert"),
499 v8::Local
<v8::FunctionTemplate
> my_ip_address_template
=
500 v8::FunctionTemplate::New(isolate_
, &MyIpAddressCallback
, v8_this
);
501 global_template
->Set(ASCIILiteralToV8String(isolate_
, "myIpAddress"),
502 my_ip_address_template
);
504 v8::Local
<v8::FunctionTemplate
> dns_resolve_template
=
505 v8::FunctionTemplate::New(isolate_
, &DnsResolveCallback
, v8_this
);
506 global_template
->Set(ASCIILiteralToV8String(isolate_
, "dnsResolve"),
507 dns_resolve_template
);
509 v8::Local
<v8::FunctionTemplate
> is_plain_host_name_template
=
510 v8::FunctionTemplate::New(isolate_
, &IsPlainHostNameCallback
, v8_this
);
511 global_template
->Set(ASCIILiteralToV8String(isolate_
, "isPlainHostName"),
512 is_plain_host_name_template
);
514 // Microsoft's PAC extensions:
516 v8::Local
<v8::FunctionTemplate
> dns_resolve_ex_template
=
517 v8::FunctionTemplate::New(isolate_
, &DnsResolveExCallback
, v8_this
);
518 global_template
->Set(ASCIILiteralToV8String(isolate_
, "dnsResolveEx"),
519 dns_resolve_ex_template
);
521 v8::Local
<v8::FunctionTemplate
> my_ip_address_ex_template
=
522 v8::FunctionTemplate::New(isolate_
, &MyIpAddressExCallback
, v8_this
);
523 global_template
->Set(ASCIILiteralToV8String(isolate_
, "myIpAddressEx"),
524 my_ip_address_ex_template
);
526 v8::Local
<v8::FunctionTemplate
> sort_ip_address_list_template
=
527 v8::FunctionTemplate::New(isolate_
,
528 &SortIpAddressListCallback
,
530 global_template
->Set(ASCIILiteralToV8String(isolate_
, "sortIpAddressList"),
531 sort_ip_address_list_template
);
533 v8::Local
<v8::FunctionTemplate
> is_in_net_ex_template
=
534 v8::FunctionTemplate::New(isolate_
, &IsInNetExCallback
, v8_this
);
535 global_template
->Set(ASCIILiteralToV8String(isolate_
, "isInNetEx"),
536 is_in_net_ex_template
);
539 isolate_
, v8::Context::New(isolate_
, NULL
, global_template
));
541 v8::Local
<v8::Context
> context
=
542 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
543 v8::Context::Scope
ctx(context
);
545 // Add the PAC utility functions to the environment.
546 // (This script should never fail, as it is a string literal!)
547 // Note that the two string literals are concatenated.
549 ASCIILiteralToV8String(
551 PROXY_RESOLVER_SCRIPT
552 PROXY_RESOLVER_SCRIPT_EX
),
553 kPacUtilityResourceName
);
559 // Add the user's PAC code to the environment.
561 RunScript(ScriptDataToV8String(isolate_
, pac_script
), kPacResourceName
);
565 // At a minimum, the FindProxyForURL() function must be defined for this
566 // to be a legitimiate PAC script.
567 v8::Local
<v8::Value
> function
;
568 return GetFindProxyForURL(&function
);
572 int GetFindProxyForURL(v8::Local
<v8::Value
>* function
) {
573 v8::Local
<v8::Context
> context
=
574 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
576 v8::TryCatch
try_catch(isolate_
);
578 if (!context
->Global()
579 ->Get(context
, ASCIILiteralToV8String(isolate_
, "FindProxyForURL"))
580 .ToLocal(function
)) {
581 DCHECK(try_catch
.HasCaught());
582 HandleError(try_catch
.Message());
585 // The value should only be empty if an exception was thrown. Code
586 // defensively just in case.
587 DCHECK_EQ(function
->IsEmpty(), try_catch
.HasCaught());
588 if (function
->IsEmpty() || try_catch
.HasCaught()) {
589 js_bindings()->OnError(
591 base::ASCIIToUTF16("Accessing FindProxyForURL threw an exception."));
592 return ERR_PAC_SCRIPT_FAILED
;
595 if (!(*function
)->IsFunction()) {
596 js_bindings()->OnError(
597 -1, base::ASCIIToUTF16(
598 "FindProxyForURL is undefined or not a function."));
599 return ERR_PAC_SCRIPT_FAILED
;
605 // Handle an exception thrown by V8.
606 void HandleError(v8::Local
<v8::Message
> message
) {
607 v8::Local
<v8::Context
> context
=
608 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
609 base::string16 error_message
;
610 int line_number
= -1;
612 if (!message
.IsEmpty()) {
613 auto maybe
= message
->GetLineNumber(context
);
615 line_number
= maybe
.FromJust();
616 V8ObjectToUTF16String(message
->Get(), &error_message
, isolate_
);
619 js_bindings()->OnError(line_number
, error_message
);
622 // Compiles and runs |script| in the current V8 context.
623 // Returns OK on success, otherwise an error code.
624 int RunScript(v8::Local
<v8::String
> script
, const char* script_name
) {
625 v8::Local
<v8::Context
> context
=
626 v8::Local
<v8::Context
>::New(isolate_
, v8_context_
);
627 v8::TryCatch
try_catch(isolate_
);
629 // Compile the script.
630 v8::ScriptOrigin origin
=
631 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_
, script_name
));
632 v8::Local
<v8::Script
> code
;
633 if (!v8::Script::Compile(context
, script
, &origin
).ToLocal(&code
)) {
634 DCHECK(try_catch
.HasCaught());
635 HandleError(try_catch
.Message());
636 return ERR_PAC_SCRIPT_FAILED
;
640 auto result
= code
->Run(context
);
641 if (result
.IsEmpty()) {
642 DCHECK(try_catch
.HasCaught());
643 HandleError(try_catch
.Message());
644 return ERR_PAC_SCRIPT_FAILED
;
650 // V8 callback for when "alert()" is invoked by the PAC script.
651 static void AlertCallback(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
653 static_cast<Context
*>(v8::External::Cast(*args
.Data())->Value());
655 // Like firefox we assume "undefined" if no argument was specified, and
656 // disregard any arguments beyond the first.
657 base::string16 message
;
658 if (args
.Length() == 0) {
659 message
= base::ASCIIToUTF16("undefined");
661 if (!V8ObjectToUTF16String(args
[0], &message
, args
.GetIsolate()))
662 return; // toString() threw an exception.
665 context
->js_bindings()->Alert(message
);
668 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
669 static void MyIpAddressCallback(
670 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
671 DnsResolveCallbackHelper(args
, JSBindings::MY_IP_ADDRESS
);
674 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
675 static void MyIpAddressExCallback(
676 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
677 DnsResolveCallbackHelper(args
, JSBindings::MY_IP_ADDRESS_EX
);
680 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
681 static void DnsResolveCallback(
682 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
683 DnsResolveCallbackHelper(args
, JSBindings::DNS_RESOLVE
);
686 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
687 static void DnsResolveExCallback(
688 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
689 DnsResolveCallbackHelper(args
, JSBindings::DNS_RESOLVE_EX
);
692 // Shared code for implementing:
693 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
694 static void DnsResolveCallbackHelper(
695 const v8::FunctionCallbackInfo
<v8::Value
>& args
,
696 JSBindings::ResolveDnsOperation op
) {
698 static_cast<Context
*>(v8::External::Cast(*args
.Data())->Value());
700 std::string hostname
;
702 // dnsResolve() and dnsResolveEx() need at least 1 argument.
703 if (op
== JSBindings::DNS_RESOLVE
|| op
== JSBindings::DNS_RESOLVE_EX
) {
704 if (!GetHostnameArgument(args
, &hostname
)) {
705 if (op
== JSBindings::DNS_RESOLVE
)
706 args
.GetReturnValue().SetNull();
713 bool terminate
= false;
716 v8::Unlocker
unlocker(args
.GetIsolate());
717 success
= context
->js_bindings()->ResolveDns(
718 hostname
, op
, &result
, &terminate
);
722 args
.GetIsolate()->TerminateExecution();
725 args
.GetReturnValue().Set(
726 ASCIIStringToV8String(args
.GetIsolate(), result
));
730 // Each function handles resolution errors differently.
732 case JSBindings::DNS_RESOLVE
:
733 args
.GetReturnValue().SetNull();
735 case JSBindings::DNS_RESOLVE_EX
:
736 args
.GetReturnValue().SetEmptyString();
738 case JSBindings::MY_IP_ADDRESS
:
739 args
.GetReturnValue().Set(
740 ASCIILiteralToV8String(args
.GetIsolate(), "127.0.0.1"));
742 case JSBindings::MY_IP_ADDRESS_EX
:
743 args
.GetReturnValue().SetEmptyString();
750 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
751 static void SortIpAddressListCallback(
752 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
753 // We need at least one string argument.
754 if (args
.Length() == 0 || args
[0].IsEmpty() || !args
[0]->IsString()) {
755 args
.GetReturnValue().SetNull();
759 std::string ip_address_list
=
760 V8StringToUTF8(v8::Local
<v8::String
>::Cast(args
[0]));
761 if (!base::IsStringASCII(ip_address_list
)) {
762 args
.GetReturnValue().SetNull();
765 std::string sorted_ip_address_list
;
766 bool success
= SortIpAddressList(ip_address_list
, &sorted_ip_address_list
);
768 args
.GetReturnValue().Set(false);
771 args
.GetReturnValue().Set(
772 ASCIIStringToV8String(args
.GetIsolate(), sorted_ip_address_list
));
775 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
776 static void IsInNetExCallback(
777 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
778 // We need at least 2 string arguments.
779 if (args
.Length() < 2 || args
[0].IsEmpty() || !args
[0]->IsString() ||
780 args
[1].IsEmpty() || !args
[1]->IsString()) {
781 args
.GetReturnValue().SetNull();
785 std::string ip_address
=
786 V8StringToUTF8(v8::Local
<v8::String
>::Cast(args
[0]));
787 if (!base::IsStringASCII(ip_address
)) {
788 args
.GetReturnValue().Set(false);
791 std::string ip_prefix
=
792 V8StringToUTF8(v8::Local
<v8::String
>::Cast(args
[1]));
793 if (!base::IsStringASCII(ip_prefix
)) {
794 args
.GetReturnValue().Set(false);
797 args
.GetReturnValue().Set(IsInNetEx(ip_address
, ip_prefix
));
800 // V8 callback for when "isPlainHostName()" is invoked by the PAC script.
801 static void IsPlainHostNameCallback(
802 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
803 // Need at least 1 string arguments.
804 if (args
.Length() < 1 || args
[0].IsEmpty() || !args
[0]->IsString()) {
805 args
.GetIsolate()->ThrowException(
806 v8::Exception::TypeError(ASCIIStringToV8String(
807 args
.GetIsolate(), "Requires 1 string parameter")));
811 std::string hostname_utf8
=
812 V8StringToUTF8(v8::Local
<v8::String
>::Cast(args
[0]));
813 args
.GetReturnValue().Set(IsPlainHostName(hostname_utf8
));
816 mutable base::Lock lock_
;
817 ProxyResolverV8::JSBindings
* js_bindings_
;
818 v8::Isolate
* isolate_
;
819 v8::Persistent
<v8::External
> v8_this_
;
820 v8::Persistent
<v8::Context
> v8_context_
;
823 // ProxyResolverV8 ------------------------------------------------------------
825 ProxyResolverV8::ProxyResolverV8(scoped_ptr
<Context
> context
)
826 : context_(context
.Pass()) {
830 ProxyResolverV8::~ProxyResolverV8() {}
832 int ProxyResolverV8::GetProxyForURL(const GURL
& query_url
,
834 ProxyResolverV8::JSBindings
* bindings
) {
835 return context_
->ResolveProxy(query_url
, results
, bindings
);
839 int ProxyResolverV8::Create(
840 const scoped_refptr
<ProxyResolverScriptData
>& script_data
,
841 ProxyResolverV8::JSBindings
* js_bindings
,
842 scoped_ptr
<ProxyResolverV8
>* resolver
) {
843 DCHECK(script_data
.get());
846 if (script_data
->utf16().empty())
847 return ERR_PAC_SCRIPT_FAILED
;
849 // Try parsing the PAC script.
850 scoped_ptr
<Context
> context(
851 new Context(g_isolate_factory
.Get().GetSharedIsolate()));
852 int rv
= context
->InitV8(script_data
, js_bindings
);
854 resolver
->reset(new ProxyResolverV8(context
.Pass()));
859 size_t ProxyResolverV8::GetTotalHeapSize() {
860 v8::Isolate
* isolate
=
861 g_isolate_factory
.Get().GetSharedIsolateWithoutCreating();
865 v8::Locker
locked(isolate
);
866 v8::Isolate::Scope
isolate_scope(isolate
);
867 v8::HeapStatistics heap_statistics
;
868 isolate
->GetHeapStatistics(&heap_statistics
);
869 return heap_statistics
.total_heap_size();
873 size_t ProxyResolverV8::GetUsedHeapSize() {
874 v8::Isolate
* isolate
=
875 g_isolate_factory
.Get().GetSharedIsolateWithoutCreating();
879 v8::Locker
locked(isolate
);
880 v8::Isolate::Scope
isolate_scope(isolate
);
881 v8::HeapStatistics heap_statistics
;
882 isolate
->GetHeapStatistics(&heap_statistics
);
883 return heap_statistics
.used_heap_size();