Remove implicit conversions from scoped_refptr to T* in content/common/gpu
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8.cc
blob37f31bfddd9aef4711e0164eee003a92d65edd72
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"
7 #include <algorithm>
8 #include <cstdio>
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/array_buffer.h"
19 #include "gin/public/isolate_holder.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_log.h"
22 #include "net/base/net_util.h"
23 #include "net/proxy/proxy_info.h"
24 #include "net/proxy/proxy_resolver_script.h"
25 #include "url/gurl.h"
26 #include "url/url_canon.h"
27 #include "v8/include/v8.h"
29 // Notes on the javascript environment:
31 // For the majority of the PAC utility functions, we use the same code
32 // as Firefox. See the javascript library that proxy_resolver_scipt.h
33 // pulls in.
35 // In addition, we implement a subset of Microsoft's extensions to PAC.
36 // - myIpAddressEx()
37 // - dnsResolveEx()
38 // - isResolvableEx()
39 // - isInNetEx()
40 // - sortIpAddressList()
42 // It is worth noting that the original PAC specification does not describe
43 // the return values on failure. Consequently, there are compatibility
44 // differences between browsers on what to return on failure, which are
45 // illustrated below:
47 // --------------------+-------------+-------------------+--------------
48 // | Firefox3 | InternetExplorer8 | --> Us <---
49 // --------------------+-------------+-------------------+--------------
50 // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
51 // dnsResolve() | null | false | null
52 // myIpAddressEx() | N/A | "" | ""
53 // sortIpAddressList() | N/A | false | false
54 // dnsResolveEx() | N/A | "" | ""
55 // isInNetEx() | N/A | false | false
56 // --------------------+-------------+-------------------+--------------
58 // TODO(eroman): The cell above reading ??? means I didn't test it.
60 // Another difference is in how dnsResolve() and myIpAddress() are
61 // implemented -- whether they should restrict to IPv4 results, or
62 // include both IPv4 and IPv6. The following table illustrates the
63 // differences:
65 // --------------------+-------------+-------------------+--------------
66 // | Firefox3 | InternetExplorer8 | --> Us <---
67 // --------------------+-------------+-------------------+--------------
68 // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
69 // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
70 // isResolvable() | IPv4/IPv6 | IPv4 | IPv4
71 // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
72 // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
73 // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
74 // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
75 // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
76 // -----------------+-------------+-------------------+--------------
78 namespace net {
80 namespace {
82 // Pseudo-name for the PAC script.
83 const char kPacResourceName[] = "proxy-pac-script.js";
84 // Pseudo-name for the PAC utility script.
85 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
87 // External string wrapper so V8 can access the UTF16 string wrapped by
88 // ProxyResolverScriptData.
89 class V8ExternalStringFromScriptData
90 : public v8::String::ExternalStringResource {
91 public:
92 explicit V8ExternalStringFromScriptData(
93 const scoped_refptr<ProxyResolverScriptData>& script_data)
94 : script_data_(script_data) {}
96 virtual const uint16_t* data() const OVERRIDE {
97 return reinterpret_cast<const uint16*>(script_data_->utf16().data());
100 virtual size_t length() const OVERRIDE {
101 return script_data_->utf16().size();
104 private:
105 const scoped_refptr<ProxyResolverScriptData> script_data_;
106 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
109 // External string wrapper so V8 can access a string literal.
110 class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
111 public:
112 // |ascii| must be a NULL-terminated C string, and must remain valid
113 // throughout this object's lifetime.
114 V8ExternalASCIILiteral(const char* ascii, size_t length)
115 : ascii_(ascii), length_(length) {
116 DCHECK(base::IsStringASCII(ascii));
119 virtual const char* data() const OVERRIDE {
120 return ascii_;
123 virtual size_t length() const OVERRIDE {
124 return length_;
127 private:
128 const char* ascii_;
129 size_t length_;
130 DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
133 // When creating a v8::String from a C++ string we have two choices: create
134 // a copy, or create a wrapper that shares the same underlying storage.
135 // For small strings it is better to just make a copy, whereas for large
136 // strings there are savings by sharing the storage. This number identifies
137 // the cutoff length for when to start wrapping rather than creating copies.
138 const size_t kMaxStringBytesForCopy = 256;
140 // Converts a V8 String to a UTF8 std::string.
141 std::string V8StringToUTF8(v8::Handle<v8::String> s) {
142 int len = s->Length();
143 std::string result;
144 if (len > 0)
145 s->WriteUtf8(WriteInto(&result, len + 1));
146 return result;
149 // Converts a V8 String to a UTF16 base::string16.
150 base::string16 V8StringToUTF16(v8::Handle<v8::String> s) {
151 int len = s->Length();
152 base::string16 result;
153 // Note that the reinterpret cast is because on Windows string16 is an alias
154 // to wstring, and hence has character type wchar_t not uint16_t.
155 if (len > 0)
156 s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
157 return result;
160 // Converts an ASCII std::string to a V8 string.
161 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate,
162 const std::string& s) {
163 DCHECK(base::IsStringASCII(s));
164 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString,
165 s.size());
168 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
169 // V8 string.
170 v8::Local<v8::String> ScriptDataToV8String(
171 v8::Isolate* isolate, const scoped_refptr<ProxyResolverScriptData>& s) {
172 if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
173 return v8::String::NewFromTwoByte(
174 isolate,
175 reinterpret_cast<const uint16_t*>(s->utf16().data()),
176 v8::String::kNormalString,
177 s->utf16().size());
179 return v8::String::NewExternal(isolate,
180 new V8ExternalStringFromScriptData(s));
183 // Converts an ASCII string literal to a V8 string.
184 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate,
185 const char* ascii) {
186 DCHECK(base::IsStringASCII(ascii));
187 size_t length = strlen(ascii);
188 if (length <= kMaxStringBytesForCopy)
189 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString,
190 length);
191 return v8::String::NewExternal(isolate,
192 new V8ExternalASCIILiteral(ascii, length));
195 // Stringizes a V8 object by calling its toString() method. Returns true
196 // on success. This may fail if the toString() throws an exception.
197 bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
198 base::string16* utf16_result,
199 v8::Isolate* isolate) {
200 if (object.IsEmpty())
201 return false;
203 v8::HandleScope scope(isolate);
204 v8::Local<v8::String> str_object = object->ToString();
205 if (str_object.IsEmpty())
206 return false;
207 *utf16_result = V8StringToUTF16(str_object);
208 return true;
211 // Extracts an hostname argument from |args|. On success returns true
212 // and fills |*hostname| with the result.
213 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
214 std::string* hostname) {
215 // The first argument should be a string.
216 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
217 return false;
219 const base::string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
221 // If the hostname is already in ASCII, simply return it as is.
222 if (base::IsStringASCII(hostname_utf16)) {
223 *hostname = base::UTF16ToASCII(hostname_utf16);
224 return true;
227 // Otherwise try to convert it from IDN to punycode.
228 const int kInitialBufferSize = 256;
229 url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output;
230 if (!url::IDNToASCII(hostname_utf16.data(), hostname_utf16.length(),
231 &punycode_output)) {
232 return false;
235 // |punycode_output| should now be ASCII; convert it to a std::string.
236 // (We could use UTF16ToASCII() instead, but that requires an extra string
237 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
238 bool success = base::UTF16ToUTF8(punycode_output.data(),
239 punycode_output.length(),
240 hostname);
241 DCHECK(success);
242 DCHECK(base::IsStringASCII(*hostname));
243 return success;
246 // Wrapper for passing around IP address strings and IPAddressNumber objects.
247 struct IPAddress {
248 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
249 : string_value(ip_string),
250 ip_address_number(ip_number) {
253 // Used for sorting IP addresses in ascending order in SortIpAddressList().
254 // IP6 addresses are placed ahead of IPv4 addresses.
255 bool operator<(const IPAddress& rhs) const {
256 const IPAddressNumber& ip1 = this->ip_address_number;
257 const IPAddressNumber& ip2 = rhs.ip_address_number;
258 if (ip1.size() != ip2.size())
259 return ip1.size() > ip2.size(); // IPv6 before IPv4.
260 DCHECK(ip1.size() == ip2.size());
261 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
264 std::string string_value;
265 IPAddressNumber ip_address_number;
268 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
269 // semi-colon delimited string containing IP addresses.
270 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
271 // IP addresses or an empty string if unable to sort the IP address list.
272 // Returns 'true' if the sorting was successful, and 'false' if the input was an
273 // empty string, a string of separators (";" in this case), or if any of the IP
274 // addresses in the input list failed to parse.
275 bool SortIpAddressList(const std::string& ip_address_list,
276 std::string* sorted_ip_address_list) {
277 sorted_ip_address_list->clear();
279 // Strip all whitespace (mimics IE behavior).
280 std::string cleaned_ip_address_list;
281 base::RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
282 if (cleaned_ip_address_list.empty())
283 return false;
285 // Split-up IP addresses and store them in a vector.
286 std::vector<IPAddress> ip_vector;
287 IPAddressNumber ip_num;
288 base::StringTokenizer str_tok(cleaned_ip_address_list, ";");
289 while (str_tok.GetNext()) {
290 if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
291 return false;
292 ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
295 if (ip_vector.empty()) // Can happen if we have something like
296 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
298 DCHECK(!ip_vector.empty());
300 // Sort lists according to ascending numeric value.
301 if (ip_vector.size() > 1)
302 std::stable_sort(ip_vector.begin(), ip_vector.end());
304 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
305 // IPv4).
306 for (size_t i = 0; i < ip_vector.size(); ++i) {
307 if (i > 0)
308 *sorted_ip_address_list += ";";
309 *sorted_ip_address_list += ip_vector[i].string_value;
311 return true;
314 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
315 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
316 // slash-delimited IP prefix with the top 'n' bits specified in the bit
317 // field. This returns 'true' if the address is in the same subnet, and
318 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
319 // format, or if an address and prefix of different types are used (e.g. IPv6
320 // address and IPv4 prefix).
321 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
322 IPAddressNumber address;
323 if (!ParseIPLiteralToNumber(ip_address, &address))
324 return false;
326 IPAddressNumber prefix;
327 size_t prefix_length_in_bits;
328 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
329 return false;
331 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
332 if (address.size() != prefix.size())
333 return false;
335 DCHECK((address.size() == 4 && prefix.size() == 4) ||
336 (address.size() == 16 && prefix.size() == 16));
338 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
341 } // namespace
343 // ProxyResolverV8::Context ---------------------------------------------------
345 class ProxyResolverV8::Context {
346 public:
347 Context(ProxyResolverV8* parent, v8::Isolate* isolate)
348 : parent_(parent),
349 isolate_(isolate) {
350 DCHECK(isolate);
353 ~Context() {
354 v8::Locker locked(isolate_);
355 v8::Isolate::Scope isolate_scope(isolate_);
357 v8_this_.Reset();
358 v8_context_.Reset();
361 JSBindings* js_bindings() {
362 return parent_->js_bindings_;
365 int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
366 v8::Locker locked(isolate_);
367 v8::Isolate::Scope isolate_scope(isolate_);
368 v8::HandleScope scope(isolate_);
370 v8::Local<v8::Context> context =
371 v8::Local<v8::Context>::New(isolate_, v8_context_);
372 v8::Context::Scope function_scope(context);
374 v8::Local<v8::Value> function;
375 if (!GetFindProxyForURL(&function)) {
376 js_bindings()->OnError(
377 -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
378 return ERR_PAC_SCRIPT_FAILED;
381 v8::Handle<v8::Value> argv[] = {
382 ASCIIStringToV8String(isolate_, query_url.spec()),
383 ASCIIStringToV8String(isolate_, query_url.HostNoBrackets()),
386 v8::TryCatch try_catch;
387 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
388 context->Global(), arraysize(argv), argv);
390 if (try_catch.HasCaught()) {
391 HandleError(try_catch.Message());
392 return ERR_PAC_SCRIPT_FAILED;
395 if (!ret->IsString()) {
396 js_bindings()->OnError(
397 -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
398 return ERR_PAC_SCRIPT_FAILED;
401 base::string16 ret_str = V8StringToUTF16(ret->ToString());
403 if (!base::IsStringASCII(ret_str)) {
404 // TODO(eroman): Rather than failing when a wide string is returned, we
405 // could extend the parsing to handle IDNA hostnames by
406 // converting them to ASCII punycode.
407 // crbug.com/47234
408 base::string16 error_message =
409 base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
410 "(crbug.com/47234): ") + ret_str;
411 js_bindings()->OnError(-1, error_message);
412 return ERR_PAC_SCRIPT_FAILED;
415 results->UsePacString(base::UTF16ToASCII(ret_str));
416 return OK;
419 int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
420 v8::Locker locked(isolate_);
421 v8::Isolate::Scope isolate_scope(isolate_);
422 v8::HandleScope scope(isolate_);
424 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
425 v8::Local<v8::External> v8_this =
426 v8::Local<v8::External>::New(isolate_, v8_this_);
427 v8::Local<v8::ObjectTemplate> global_template =
428 v8::ObjectTemplate::New(isolate_);
430 // Attach the javascript bindings.
431 v8::Local<v8::FunctionTemplate> alert_template =
432 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
433 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"),
434 alert_template);
436 v8::Local<v8::FunctionTemplate> my_ip_address_template =
437 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
438 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
439 my_ip_address_template);
441 v8::Local<v8::FunctionTemplate> dns_resolve_template =
442 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
443 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
444 dns_resolve_template);
446 // Microsoft's PAC extensions:
448 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
449 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
450 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
451 dns_resolve_ex_template);
453 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
454 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
455 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
456 my_ip_address_ex_template);
458 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
459 v8::FunctionTemplate::New(isolate_,
460 &SortIpAddressListCallback,
461 v8_this);
462 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
463 sort_ip_address_list_template);
465 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
466 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
467 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
468 is_in_net_ex_template);
470 v8_context_.Reset(
471 isolate_, v8::Context::New(isolate_, NULL, global_template));
473 v8::Local<v8::Context> context =
474 v8::Local<v8::Context>::New(isolate_, v8_context_);
475 v8::Context::Scope ctx(context);
477 // Add the PAC utility functions to the environment.
478 // (This script should never fail, as it is a string literal!)
479 // Note that the two string literals are concatenated.
480 int rv = RunScript(
481 ASCIILiteralToV8String(
482 isolate_,
483 PROXY_RESOLVER_SCRIPT
484 PROXY_RESOLVER_SCRIPT_EX),
485 kPacUtilityResourceName);
486 if (rv != OK) {
487 NOTREACHED();
488 return rv;
491 // Add the user's PAC code to the environment.
492 rv =
493 RunScript(ScriptDataToV8String(isolate_, pac_script), kPacResourceName);
494 if (rv != OK)
495 return rv;
497 // At a minimum, the FindProxyForURL() function must be defined for this
498 // to be a legitimiate PAC script.
499 v8::Local<v8::Value> function;
500 if (!GetFindProxyForURL(&function)) {
501 js_bindings()->OnError(
502 -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
503 return ERR_PAC_SCRIPT_FAILED;
506 return OK;
509 private:
510 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
511 v8::Local<v8::Context> context =
512 v8::Local<v8::Context>::New(isolate_, v8_context_);
513 *function =
514 context->Global()->Get(
515 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
516 return (*function)->IsFunction();
519 // Handle an exception thrown by V8.
520 void HandleError(v8::Handle<v8::Message> message) {
521 base::string16 error_message;
522 int line_number = -1;
524 if (!message.IsEmpty()) {
525 line_number = message->GetLineNumber();
526 V8ObjectToUTF16String(message->Get(), &error_message, isolate_);
529 js_bindings()->OnError(line_number, error_message);
532 // Compiles and runs |script| in the current V8 context.
533 // Returns OK on success, otherwise an error code.
534 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
535 v8::TryCatch try_catch;
537 // Compile the script.
538 v8::ScriptOrigin origin =
539 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
540 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
542 // Execute.
543 if (!code.IsEmpty())
544 code->Run();
546 // Check for errors.
547 if (try_catch.HasCaught()) {
548 HandleError(try_catch.Message());
549 return ERR_PAC_SCRIPT_FAILED;
552 return OK;
555 // V8 callback for when "alert()" is invoked by the PAC script.
556 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
557 Context* context =
558 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
560 // Like firefox we assume "undefined" if no argument was specified, and
561 // disregard any arguments beyond the first.
562 base::string16 message;
563 if (args.Length() == 0) {
564 message = base::ASCIIToUTF16("undefined");
565 } else {
566 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
567 return; // toString() threw an exception.
570 context->js_bindings()->Alert(message);
573 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
574 static void MyIpAddressCallback(
575 const v8::FunctionCallbackInfo<v8::Value>& args) {
576 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
579 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
580 static void MyIpAddressExCallback(
581 const v8::FunctionCallbackInfo<v8::Value>& args) {
582 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
585 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
586 static void DnsResolveCallback(
587 const v8::FunctionCallbackInfo<v8::Value>& args) {
588 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
591 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
592 static void DnsResolveExCallback(
593 const v8::FunctionCallbackInfo<v8::Value>& args) {
594 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
597 // Shared code for implementing:
598 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
599 static void DnsResolveCallbackHelper(
600 const v8::FunctionCallbackInfo<v8::Value>& args,
601 JSBindings::ResolveDnsOperation op) {
602 Context* context =
603 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
605 std::string hostname;
607 // dnsResolve() and dnsResolveEx() need at least 1 argument.
608 if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
609 if (!GetHostnameArgument(args, &hostname)) {
610 if (op == JSBindings::DNS_RESOLVE)
611 args.GetReturnValue().SetNull();
612 return;
616 std::string result;
617 bool success;
618 bool terminate = false;
621 v8::Unlocker unlocker(args.GetIsolate());
622 success = context->js_bindings()->ResolveDns(
623 hostname, op, &result, &terminate);
626 if (terminate)
627 v8::V8::TerminateExecution(args.GetIsolate());
629 if (success) {
630 args.GetReturnValue().Set(
631 ASCIIStringToV8String(args.GetIsolate(), result));
632 return;
635 // Each function handles resolution errors differently.
636 switch (op) {
637 case JSBindings::DNS_RESOLVE:
638 args.GetReturnValue().SetNull();
639 return;
640 case JSBindings::DNS_RESOLVE_EX:
641 args.GetReturnValue().SetEmptyString();
642 return;
643 case JSBindings::MY_IP_ADDRESS:
644 args.GetReturnValue().Set(
645 ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
646 return;
647 case JSBindings::MY_IP_ADDRESS_EX:
648 args.GetReturnValue().SetEmptyString();
649 return;
652 NOTREACHED();
655 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
656 static void SortIpAddressListCallback(
657 const v8::FunctionCallbackInfo<v8::Value>& args) {
658 // We need at least one string argument.
659 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
660 args.GetReturnValue().SetNull();
661 return;
664 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
665 if (!base::IsStringASCII(ip_address_list)) {
666 args.GetReturnValue().SetNull();
667 return;
669 std::string sorted_ip_address_list;
670 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
671 if (!success) {
672 args.GetReturnValue().Set(false);
673 return;
675 args.GetReturnValue().Set(
676 ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
679 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
680 static void IsInNetExCallback(
681 const v8::FunctionCallbackInfo<v8::Value>& args) {
682 // We need at least 2 string arguments.
683 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
684 args[1].IsEmpty() || !args[1]->IsString()) {
685 args.GetReturnValue().SetNull();
686 return;
689 std::string ip_address = V8StringToUTF8(args[0]->ToString());
690 if (!base::IsStringASCII(ip_address)) {
691 args.GetReturnValue().Set(false);
692 return;
694 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
695 if (!base::IsStringASCII(ip_prefix)) {
696 args.GetReturnValue().Set(false);
697 return;
699 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
702 mutable base::Lock lock_;
703 ProxyResolverV8* parent_;
704 v8::Isolate* isolate_;
705 v8::Persistent<v8::External> v8_this_;
706 v8::Persistent<v8::Context> v8_context_;
709 // ProxyResolverV8 ------------------------------------------------------------
711 ProxyResolverV8::ProxyResolverV8()
712 : ProxyResolver(true /*expects_pac_bytes*/),
713 js_bindings_(NULL) {
716 ProxyResolverV8::~ProxyResolverV8() {}
718 int ProxyResolverV8::GetProxyForURL(
719 const GURL& query_url, ProxyInfo* results,
720 const CompletionCallback& /*callback*/,
721 RequestHandle* /*request*/,
722 const BoundNetLog& net_log) {
723 DCHECK(js_bindings_);
725 // If the V8 instance has not been initialized (either because
726 // SetPacScript() wasn't called yet, or because it failed.
727 if (!context_)
728 return ERR_FAILED;
730 // Otherwise call into V8.
731 int rv = context_->ResolveProxy(query_url, results);
733 return rv;
736 void ProxyResolverV8::CancelRequest(RequestHandle request) {
737 // This is a synchronous ProxyResolver; no possibility for async requests.
738 NOTREACHED();
741 LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
742 NOTREACHED();
743 return LOAD_STATE_IDLE;
746 void ProxyResolverV8::CancelSetPacScript() {
747 NOTREACHED();
750 int ProxyResolverV8::SetPacScript(
751 const scoped_refptr<ProxyResolverScriptData>& script_data,
752 const CompletionCallback& /*callback*/) {
753 DCHECK(script_data.get());
754 DCHECK(js_bindings_);
756 context_.reset();
757 if (script_data->utf16().empty())
758 return ERR_PAC_SCRIPT_FAILED;
760 // Try parsing the PAC script.
761 scoped_ptr<Context> context(new Context(this, GetDefaultIsolate()));
762 int rv = context->InitV8(script_data);
763 if (rv == OK)
764 context_.reset(context.release());
765 return rv;
768 // static
769 void ProxyResolverV8::EnsureIsolateCreated() {
770 if (g_proxy_resolver_isolate_)
771 return;
772 gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode,
773 gin::ArrayBufferAllocator::SharedInstance());
774 g_proxy_resolver_isolate_ = new gin::IsolateHolder;
775 ANNOTATE_LEAKING_OBJECT_PTR(g_proxy_resolver_isolate_);
778 // static
779 v8::Isolate* ProxyResolverV8::GetDefaultIsolate() {
780 DCHECK(g_proxy_resolver_isolate_)
781 << "Must call ProxyResolverV8::EnsureIsolateCreated() first";
782 return g_proxy_resolver_isolate_->isolate();
785 gin::IsolateHolder* ProxyResolverV8::g_proxy_resolver_isolate_ = NULL;
787 // static
788 size_t ProxyResolverV8::GetTotalHeapSize() {
789 if (!g_proxy_resolver_isolate_)
790 return 0;
792 v8::Locker locked(g_proxy_resolver_isolate_->isolate());
793 v8::Isolate::Scope isolate_scope(g_proxy_resolver_isolate_->isolate());
794 v8::HeapStatistics heap_statistics;
795 g_proxy_resolver_isolate_->isolate()->GetHeapStatistics(&heap_statistics);
796 return heap_statistics.total_heap_size();
799 // static
800 size_t ProxyResolverV8::GetUsedHeapSize() {
801 if (!g_proxy_resolver_isolate_)
802 return 0;
804 v8::Locker locked(g_proxy_resolver_isolate_->isolate());
805 v8::Isolate::Scope isolate_scope(g_proxy_resolver_isolate_->isolate());
806 v8::HeapStatistics heap_statistics;
807 g_proxy_resolver_isolate_->isolate()->GetHeapStatistics(&heap_statistics);
808 return heap_statistics.used_heap_size();
811 } // namespace net