Dismiss autofill popup on screen orientation change.
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8.cc
blob87f61028039787c81b997e3b3f8736f6d88c0129
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/logging.h"
13 #include "base/strings/string_tokenizer.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/synchronization/lock.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/net_log.h"
19 #include "net/base/net_util.h"
20 #include "net/proxy/proxy_info.h"
21 #include "net/proxy/proxy_resolver_script.h"
22 #include "url/gurl.h"
23 #include "url/url_canon.h"
24 #include "v8/include/v8.h"
26 // Notes on the javascript environment:
28 // For the majority of the PAC utility functions, we use the same code
29 // as Firefox. See the javascript library that proxy_resolver_scipt.h
30 // pulls in.
32 // In addition, we implement a subset of Microsoft's extensions to PAC.
33 // - myIpAddressEx()
34 // - dnsResolveEx()
35 // - isResolvableEx()
36 // - isInNetEx()
37 // - sortIpAddressList()
39 // It is worth noting that the original PAC specification does not describe
40 // the return values on failure. Consequently, there are compatibility
41 // differences between browsers on what to return on failure, which are
42 // illustrated below:
44 // --------------------+-------------+-------------------+--------------
45 // | Firefox3 | InternetExplorer8 | --> Us <---
46 // --------------------+-------------+-------------------+--------------
47 // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
48 // dnsResolve() | null | false | null
49 // myIpAddressEx() | N/A | "" | ""
50 // sortIpAddressList() | N/A | false | false
51 // dnsResolveEx() | N/A | "" | ""
52 // isInNetEx() | N/A | false | false
53 // --------------------+-------------+-------------------+--------------
55 // TODO(eroman): The cell above reading ??? means I didn't test it.
57 // Another difference is in how dnsResolve() and myIpAddress() are
58 // implemented -- whether they should restrict to IPv4 results, or
59 // include both IPv4 and IPv6. The following table illustrates the
60 // differences:
62 // --------------------+-------------+-------------------+--------------
63 // | Firefox3 | InternetExplorer8 | --> Us <---
64 // --------------------+-------------+-------------------+--------------
65 // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
66 // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
67 // isResolvable() | IPv4/IPv6 | IPv4 | IPv4
68 // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
69 // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
70 // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
71 // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
72 // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
73 // -----------------+-------------+-------------------+--------------
75 namespace net {
77 namespace {
79 // Pseudo-name for the PAC script.
80 const char kPacResourceName[] = "proxy-pac-script.js";
81 // Pseudo-name for the PAC utility script.
82 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
84 // External string wrapper so V8 can access the UTF16 string wrapped by
85 // ProxyResolverScriptData.
86 class V8ExternalStringFromScriptData
87 : public v8::String::ExternalStringResource {
88 public:
89 explicit V8ExternalStringFromScriptData(
90 const scoped_refptr<ProxyResolverScriptData>& script_data)
91 : script_data_(script_data) {}
93 virtual const uint16_t* data() const OVERRIDE {
94 return reinterpret_cast<const uint16*>(script_data_->utf16().data());
97 virtual size_t length() const OVERRIDE {
98 return script_data_->utf16().size();
101 private:
102 const scoped_refptr<ProxyResolverScriptData> script_data_;
103 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
106 // External string wrapper so V8 can access a string literal.
107 class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
108 public:
109 // |ascii| must be a NULL-terminated C string, and must remain valid
110 // throughout this object's lifetime.
111 V8ExternalASCIILiteral(const char* ascii, size_t length)
112 : ascii_(ascii), length_(length) {
113 DCHECK(IsStringASCII(ascii));
116 virtual const char* data() const OVERRIDE {
117 return ascii_;
120 virtual size_t length() const OVERRIDE {
121 return length_;
124 private:
125 const char* ascii_;
126 size_t length_;
127 DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
130 // When creating a v8::String from a C++ string we have two choices: create
131 // a copy, or create a wrapper that shares the same underlying storage.
132 // For small strings it is better to just make a copy, whereas for large
133 // strings there are savings by sharing the storage. This number identifies
134 // the cutoff length for when to start wrapping rather than creating copies.
135 const size_t kMaxStringBytesForCopy = 256;
137 // Converts a V8 String to a UTF8 std::string.
138 std::string V8StringToUTF8(v8::Handle<v8::String> s) {
139 int len = s->Length();
140 std::string result;
141 if (len > 0)
142 s->WriteUtf8(WriteInto(&result, len + 1));
143 return result;
146 // Converts a V8 String to a UTF16 base::string16.
147 base::string16 V8StringToUTF16(v8::Handle<v8::String> s) {
148 int len = s->Length();
149 base::string16 result;
150 // Note that the reinterpret cast is because on Windows string16 is an alias
151 // to wstring, and hence has character type wchar_t not uint16_t.
152 if (len > 0)
153 s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
154 return result;
157 // Converts an ASCII std::string to a V8 string.
158 v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
159 DCHECK(IsStringASCII(s));
160 return v8::String::New(s.data(), s.size());
163 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
164 // V8 string.
165 v8::Local<v8::String> ScriptDataToV8String(
166 const scoped_refptr<ProxyResolverScriptData>& s) {
167 if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
168 return v8::String::New(
169 reinterpret_cast<const uint16_t*>(s->utf16().data()),
170 s->utf16().size());
172 return v8::String::NewExternal(new V8ExternalStringFromScriptData(s));
175 // Converts an ASCII string literal to a V8 string.
176 v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
177 DCHECK(IsStringASCII(ascii));
178 size_t length = strlen(ascii);
179 if (length <= kMaxStringBytesForCopy)
180 return v8::String::New(ascii, length);
181 return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
184 // Stringizes a V8 object by calling its toString() method. Returns true
185 // on success. This may fail if the toString() throws an exception.
186 bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
187 base::string16* utf16_result) {
188 if (object.IsEmpty())
189 return false;
191 v8::HandleScope scope;
192 v8::Local<v8::String> str_object = object->ToString();
193 if (str_object.IsEmpty())
194 return false;
195 *utf16_result = V8StringToUTF16(str_object);
196 return true;
199 // Extracts an hostname argument from |args|. On success returns true
200 // and fills |*hostname| with the result.
201 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
202 std::string* hostname) {
203 // The first argument should be a string.
204 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
205 return false;
207 const base::string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
209 // If the hostname is already in ASCII, simply return it as is.
210 if (IsStringASCII(hostname_utf16)) {
211 *hostname = UTF16ToASCII(hostname_utf16);
212 return true;
215 // Otherwise try to convert it from IDN to punycode.
216 const int kInitialBufferSize = 256;
217 url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode_output;
218 if (!url_canon::IDNToASCII(hostname_utf16.data(),
219 hostname_utf16.length(),
220 &punycode_output)) {
221 return false;
224 // |punycode_output| should now be ASCII; convert it to a std::string.
225 // (We could use UTF16ToASCII() instead, but that requires an extra string
226 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
227 bool success = UTF16ToUTF8(punycode_output.data(),
228 punycode_output.length(),
229 hostname);
230 DCHECK(success);
231 DCHECK(IsStringASCII(*hostname));
232 return success;
235 // Wrapper for passing around IP address strings and IPAddressNumber objects.
236 struct IPAddress {
237 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
238 : string_value(ip_string),
239 ip_address_number(ip_number) {
242 // Used for sorting IP addresses in ascending order in SortIpAddressList().
243 // IP6 addresses are placed ahead of IPv4 addresses.
244 bool operator<(const IPAddress& rhs) const {
245 const IPAddressNumber& ip1 = this->ip_address_number;
246 const IPAddressNumber& ip2 = rhs.ip_address_number;
247 if (ip1.size() != ip2.size())
248 return ip1.size() > ip2.size(); // IPv6 before IPv4.
249 DCHECK(ip1.size() == ip2.size());
250 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
253 std::string string_value;
254 IPAddressNumber ip_address_number;
257 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
258 // semi-colon delimited string containing IP addresses.
259 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
260 // IP addresses or an empty string if unable to sort the IP address list.
261 // Returns 'true' if the sorting was successful, and 'false' if the input was an
262 // empty string, a string of separators (";" in this case), or if any of the IP
263 // addresses in the input list failed to parse.
264 bool SortIpAddressList(const std::string& ip_address_list,
265 std::string* sorted_ip_address_list) {
266 sorted_ip_address_list->clear();
268 // Strip all whitespace (mimics IE behavior).
269 std::string cleaned_ip_address_list;
270 RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
271 if (cleaned_ip_address_list.empty())
272 return false;
274 // Split-up IP addresses and store them in a vector.
275 std::vector<IPAddress> ip_vector;
276 IPAddressNumber ip_num;
277 base::StringTokenizer str_tok(cleaned_ip_address_list, ";");
278 while (str_tok.GetNext()) {
279 if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
280 return false;
281 ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
284 if (ip_vector.empty()) // Can happen if we have something like
285 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
287 DCHECK(!ip_vector.empty());
289 // Sort lists according to ascending numeric value.
290 if (ip_vector.size() > 1)
291 std::stable_sort(ip_vector.begin(), ip_vector.end());
293 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
294 // IPv4).
295 for (size_t i = 0; i < ip_vector.size(); ++i) {
296 if (i > 0)
297 *sorted_ip_address_list += ";";
298 *sorted_ip_address_list += ip_vector[i].string_value;
300 return true;
303 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
304 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
305 // slash-delimited IP prefix with the top 'n' bits specified in the bit
306 // field. This returns 'true' if the address is in the same subnet, and
307 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
308 // format, or if an address and prefix of different types are used (e.g. IPv6
309 // address and IPv4 prefix).
310 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
311 IPAddressNumber address;
312 if (!ParseIPLiteralToNumber(ip_address, &address))
313 return false;
315 IPAddressNumber prefix;
316 size_t prefix_length_in_bits;
317 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
318 return false;
320 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
321 if (address.size() != prefix.size())
322 return false;
324 DCHECK((address.size() == 4 && prefix.size() == 4) ||
325 (address.size() == 16 && prefix.size() == 16));
327 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
330 } // namespace
332 // ProxyResolverV8::Context ---------------------------------------------------
334 class ProxyResolverV8::Context {
335 public:
336 Context(ProxyResolverV8* parent, v8::Isolate* isolate)
337 : parent_(parent),
338 isolate_(isolate) {
339 DCHECK(isolate);
342 ~Context() {
343 v8::Locker locked(isolate_);
345 v8_this_.Dispose(isolate_);
346 v8_context_.Dispose(isolate_);
349 JSBindings* js_bindings() {
350 return parent_->js_bindings_;
353 int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
354 v8::Locker locked(isolate_);
355 v8::HandleScope scope(isolate_);
357 v8::Local<v8::Context> context =
358 v8::Local<v8::Context>::New(isolate_, v8_context_);
359 v8::Context::Scope function_scope(context);
361 v8::Local<v8::Value> function;
362 if (!GetFindProxyForURL(&function)) {
363 js_bindings()->OnError(
364 -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
365 return ERR_PAC_SCRIPT_FAILED;
368 v8::Handle<v8::Value> argv[] = {
369 ASCIIStringToV8String(query_url.spec()),
370 ASCIIStringToV8String(query_url.HostNoBrackets()),
373 v8::TryCatch try_catch;
374 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
375 context->Global(), arraysize(argv), argv);
377 if (try_catch.HasCaught()) {
378 HandleError(try_catch.Message());
379 return ERR_PAC_SCRIPT_FAILED;
382 if (!ret->IsString()) {
383 js_bindings()->OnError(
384 -1, ASCIIToUTF16("FindProxyForURL() did not return a string."));
385 return ERR_PAC_SCRIPT_FAILED;
388 base::string16 ret_str = V8StringToUTF16(ret->ToString());
390 if (!IsStringASCII(ret_str)) {
391 // TODO(eroman): Rather than failing when a wide string is returned, we
392 // could extend the parsing to handle IDNA hostnames by
393 // converting them to ASCII punycode.
394 // crbug.com/47234
395 base::string16 error_message =
396 ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
397 "(crbug.com/47234): ") + ret_str;
398 js_bindings()->OnError(-1, error_message);
399 return ERR_PAC_SCRIPT_FAILED;
402 results->UsePacString(UTF16ToASCII(ret_str));
403 return OK;
406 int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
407 v8::Locker locked(isolate_);
408 v8::HandleScope scope(isolate_);
410 v8_this_.Reset(isolate_, v8::External::New(this));
411 v8::Local<v8::External> v8_this =
412 v8::Local<v8::External>::New(isolate_, v8_this_);
413 v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
415 // Attach the javascript bindings.
416 v8::Local<v8::FunctionTemplate> alert_template =
417 v8::FunctionTemplate::New(&AlertCallback, v8_this);
418 global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
420 v8::Local<v8::FunctionTemplate> my_ip_address_template =
421 v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this);
422 global_template->Set(ASCIILiteralToV8String("myIpAddress"),
423 my_ip_address_template);
425 v8::Local<v8::FunctionTemplate> dns_resolve_template =
426 v8::FunctionTemplate::New(&DnsResolveCallback, v8_this);
427 global_template->Set(ASCIILiteralToV8String("dnsResolve"),
428 dns_resolve_template);
430 // Microsoft's PAC extensions:
432 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
433 v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this);
434 global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
435 dns_resolve_ex_template);
437 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
438 v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this);
439 global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
440 my_ip_address_ex_template);
442 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
443 v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this);
444 global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
445 sort_ip_address_list_template);
447 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
448 v8::FunctionTemplate::New(&IsInNetExCallback, v8_this);
449 global_template->Set(ASCIILiteralToV8String("isInNetEx"),
450 is_in_net_ex_template);
452 v8_context_.Reset(
453 isolate_, v8::Context::New(isolate_, NULL, global_template));
455 v8::Local<v8::Context> context =
456 v8::Local<v8::Context>::New(isolate_, v8_context_);
457 v8::Context::Scope ctx(context);
459 // Add the PAC utility functions to the environment.
460 // (This script should never fail, as it is a string literal!)
461 // Note that the two string literals are concatenated.
462 int rv = RunScript(
463 ASCIILiteralToV8String(
464 PROXY_RESOLVER_SCRIPT
465 PROXY_RESOLVER_SCRIPT_EX),
466 kPacUtilityResourceName);
467 if (rv != OK) {
468 NOTREACHED();
469 return rv;
472 // Add the user's PAC code to the environment.
473 rv = RunScript(ScriptDataToV8String(pac_script), kPacResourceName);
474 if (rv != OK)
475 return rv;
477 // At a minimum, the FindProxyForURL() function must be defined for this
478 // to be a legitimiate PAC script.
479 v8::Local<v8::Value> function;
480 if (!GetFindProxyForURL(&function)) {
481 js_bindings()->OnError(
482 -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
483 return ERR_PAC_SCRIPT_FAILED;
486 return OK;
489 void PurgeMemory() {
490 v8::Locker locked(isolate_);
491 v8::V8::LowMemoryNotification();
494 private:
495 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
496 v8::Local<v8::Context> context =
497 v8::Local<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
498 *function =
499 context->Global()->Get(ASCIILiteralToV8String("FindProxyForURL"));
500 return (*function)->IsFunction();
503 // Handle an exception thrown by V8.
504 void HandleError(v8::Handle<v8::Message> message) {
505 base::string16 error_message;
506 int line_number = -1;
508 if (!message.IsEmpty()) {
509 line_number = message->GetLineNumber();
510 V8ObjectToUTF16String(message->Get(), &error_message);
513 js_bindings()->OnError(line_number, error_message);
516 // Compiles and runs |script| in the current V8 context.
517 // Returns OK on success, otherwise an error code.
518 int RunScript(v8::Handle<v8::String> script, const char* script_name) {
519 v8::TryCatch try_catch;
521 // Compile the script.
522 v8::ScriptOrigin origin =
523 v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
524 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
526 // Execute.
527 if (!code.IsEmpty())
528 code->Run();
530 // Check for errors.
531 if (try_catch.HasCaught()) {
532 HandleError(try_catch.Message());
533 return ERR_PAC_SCRIPT_FAILED;
536 return OK;
539 // V8 callback for when "alert()" is invoked by the PAC script.
540 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
541 Context* context =
542 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
544 // Like firefox we assume "undefined" if no argument was specified, and
545 // disregard any arguments beyond the first.
546 base::string16 message;
547 if (args.Length() == 0) {
548 message = ASCIIToUTF16("undefined");
549 } else {
550 if (!V8ObjectToUTF16String(args[0], &message))
551 return; // toString() threw an exception.
554 context->js_bindings()->Alert(message);
557 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
558 static void MyIpAddressCallback(
559 const v8::FunctionCallbackInfo<v8::Value>& args) {
560 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
563 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
564 static void MyIpAddressExCallback(
565 const v8::FunctionCallbackInfo<v8::Value>& args) {
566 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
569 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
570 static void DnsResolveCallback(
571 const v8::FunctionCallbackInfo<v8::Value>& args) {
572 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
575 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
576 static void DnsResolveExCallback(
577 const v8::FunctionCallbackInfo<v8::Value>& args) {
578 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
581 // Shared code for implementing:
582 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
583 static void DnsResolveCallbackHelper(
584 const v8::FunctionCallbackInfo<v8::Value>& args,
585 JSBindings::ResolveDnsOperation op) {
586 Context* context =
587 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
589 std::string hostname;
591 // dnsResolve() and dnsResolveEx() need at least 1 argument.
592 if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
593 if (!GetHostnameArgument(args, &hostname)) {
594 if (op == JSBindings::DNS_RESOLVE)
595 args.GetReturnValue().SetNull();
596 return;
600 std::string result;
601 bool success;
602 bool terminate = false;
605 v8::Unlocker unlocker(args.GetIsolate());
606 success = context->js_bindings()->ResolveDns(
607 hostname, op, &result, &terminate);
610 if (terminate)
611 v8::V8::TerminateExecution(args.GetIsolate());
613 if (success) {
614 args.GetReturnValue().Set(ASCIIStringToV8String(result));
615 return;
618 // Each function handles resolution errors differently.
619 switch (op) {
620 case JSBindings::DNS_RESOLVE:
621 args.GetReturnValue().SetNull();
622 return;
623 case JSBindings::DNS_RESOLVE_EX:
624 args.GetReturnValue().SetEmptyString();
625 return;
626 case JSBindings::MY_IP_ADDRESS:
627 args.GetReturnValue().Set(ASCIILiteralToV8String("127.0.0.1"));
628 return;
629 case JSBindings::MY_IP_ADDRESS_EX:
630 args.GetReturnValue().SetEmptyString();
631 return;
634 NOTREACHED();
637 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
638 static void SortIpAddressListCallback(
639 const v8::FunctionCallbackInfo<v8::Value>& args) {
640 // We need at least one string argument.
641 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
642 args.GetReturnValue().SetNull();
643 return;
646 std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
647 if (!IsStringASCII(ip_address_list)) {
648 args.GetReturnValue().SetNull();
649 return;
651 std::string sorted_ip_address_list;
652 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
653 if (!success) {
654 args.GetReturnValue().Set(false);
655 return;
657 args.GetReturnValue().Set(ASCIIStringToV8String(sorted_ip_address_list));
660 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
661 static void IsInNetExCallback(
662 const v8::FunctionCallbackInfo<v8::Value>& args) {
663 // We need at least 2 string arguments.
664 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
665 args[1].IsEmpty() || !args[1]->IsString()) {
666 args.GetReturnValue().SetNull();
667 return;
670 std::string ip_address = V8StringToUTF8(args[0]->ToString());
671 if (!IsStringASCII(ip_address)) {
672 args.GetReturnValue().Set(false);
673 return;
675 std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
676 if (!IsStringASCII(ip_prefix)) {
677 args.GetReturnValue().Set(false);
678 return;
680 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
683 mutable base::Lock lock_;
684 ProxyResolverV8* parent_;
685 v8::Isolate* isolate_;
686 v8::Persistent<v8::External> v8_this_;
687 v8::Persistent<v8::Context> v8_context_;
690 // ProxyResolverV8 ------------------------------------------------------------
692 ProxyResolverV8::ProxyResolverV8()
693 : ProxyResolver(true /*expects_pac_bytes*/),
694 js_bindings_(NULL) {
697 ProxyResolverV8::~ProxyResolverV8() {}
699 int ProxyResolverV8::GetProxyForURL(
700 const GURL& query_url, ProxyInfo* results,
701 const CompletionCallback& /*callback*/,
702 RequestHandle* /*request*/,
703 const BoundNetLog& net_log) {
704 DCHECK(js_bindings_);
706 // If the V8 instance has not been initialized (either because
707 // SetPacScript() wasn't called yet, or because it failed.
708 if (!context_)
709 return ERR_FAILED;
711 // Otherwise call into V8.
712 int rv = context_->ResolveProxy(query_url, results);
714 return rv;
717 void ProxyResolverV8::CancelRequest(RequestHandle request) {
718 // This is a synchronous ProxyResolver; no possibility for async requests.
719 NOTREACHED();
722 LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
723 NOTREACHED();
724 return LOAD_STATE_IDLE;
727 void ProxyResolverV8::CancelSetPacScript() {
728 NOTREACHED();
731 void ProxyResolverV8::PurgeMemory() {
732 context_->PurgeMemory();
735 int ProxyResolverV8::SetPacScript(
736 const scoped_refptr<ProxyResolverScriptData>& script_data,
737 const CompletionCallback& /*callback*/) {
738 DCHECK(script_data.get());
739 DCHECK(js_bindings_);
741 context_.reset();
742 if (script_data->utf16().empty())
743 return ERR_PAC_SCRIPT_FAILED;
745 // Try parsing the PAC script.
746 scoped_ptr<Context> context(new Context(this, GetDefaultIsolate()));
747 int rv = context->InitV8(script_data);
748 if (rv == OK)
749 context_.reset(context.release());
750 return rv;
753 // static
754 void ProxyResolverV8::RememberDefaultIsolate() {
755 v8::Isolate* isolate = v8::Isolate::GetCurrent();
756 DCHECK(isolate)
757 << "ProxyResolverV8::RememberDefaultIsolate called on wrong thread";
758 DCHECK(g_default_isolate_ == NULL || g_default_isolate_ == isolate)
759 << "Default Isolate can not be changed";
760 g_default_isolate_ = isolate;
763 #if defined(OS_WIN)
764 // static
765 void ProxyResolverV8::CreateIsolate() {
766 v8::Isolate* isolate = v8::Isolate::New();
767 DCHECK(isolate);
768 DCHECK(g_default_isolate_ == NULL) << "Default Isolate can not be set twice";
770 isolate->Enter();
771 v8::V8::Initialize();
773 g_default_isolate_ = isolate;
775 #endif // defined(OS_WIN)
777 // static
778 v8::Isolate* ProxyResolverV8::GetDefaultIsolate() {
779 DCHECK(g_default_isolate_)
780 << "Must call ProxyResolverV8::RememberDefaultIsolate() first";
781 return g_default_isolate_;
784 v8::Isolate* ProxyResolverV8::g_default_isolate_ = NULL;
786 // static
787 size_t ProxyResolverV8::GetTotalHeapSize() {
788 if (!g_default_isolate_)
789 return 0;
791 v8::Locker locked(g_default_isolate_);
792 v8::HeapStatistics heap_statistics;
793 g_default_isolate_->GetHeapStatistics(&heap_statistics);
794 return heap_statistics.total_heap_size();
797 // static
798 size_t ProxyResolverV8::GetUsedHeapSize() {
799 if (!g_default_isolate_)
800 return 0;
802 v8::Locker locked(g_default_isolate_);
803 v8::HeapStatistics heap_statistics;
804 g_default_isolate_->GetHeapStatistics(&heap_statistics);
805 return heap_statistics.used_heap_size();
808 } // namespace net