Add Wi-FI SSID to captive portal interstitial.
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8.cc
blob693fbb24837ff84b01f7ffd4bd5018c6e3b77944
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 const uint16_t* data() const override {
97 return reinterpret_cast<const uint16*>(script_data_->utf16().data());
100 size_t length() const override { return script_data_->utf16().size(); }
102 private:
103 const scoped_refptr<ProxyResolverScriptData> script_data_;
104 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
107 // External string wrapper so V8 can access a string literal.
108 class V8ExternalASCIILiteral
109 : public v8::String::ExternalOneByteStringResource {
110 public:
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 const char* data() const override { return ascii_; }
120 size_t length() const override { return length_; }
122 private:
123 const char* ascii_;
124 size_t length_;
125 DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
128 // When creating a v8::String from a C++ string we have two choices: create
129 // a copy, or create a wrapper that shares the same underlying storage.
130 // For small strings it is better to just make a copy, whereas for large
131 // strings there are savings by sharing the storage. This number identifies
132 // the cutoff length for when to start wrapping rather than creating copies.
133 const size_t kMaxStringBytesForCopy = 256;
135 // Converts a V8 String to a UTF8 std::string.
136 std::string V8StringToUTF8(v8::Local<v8::String> s) {
137 int len = s->Length();
138 std::string result;
139 if (len > 0)
140 s->WriteUtf8(WriteInto(&result, len + 1));
141 return result;
144 // Converts a V8 String to a UTF16 base::string16.
145 base::string16 V8StringToUTF16(v8::Local<v8::String> s) {
146 int len = s->Length();
147 base::string16 result;
148 // Note that the reinterpret cast is because on Windows string16 is an alias
149 // to wstring, and hence has character type wchar_t not uint16_t.
150 if (len > 0)
151 s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
152 return result;
155 // Converts an ASCII std::string to a V8 string.
156 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate,
157 const std::string& s) {
158 DCHECK(base::IsStringASCII(s));
159 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString,
160 s.size());
163 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
164 // V8 string.
165 v8::Local<v8::String> ScriptDataToV8String(
166 v8::Isolate* isolate, const scoped_refptr<ProxyResolverScriptData>& s) {
167 if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
168 return v8::String::NewFromTwoByte(
169 isolate,
170 reinterpret_cast<const uint16_t*>(s->utf16().data()),
171 v8::String::kNormalString,
172 s->utf16().size());
174 return v8::String::NewExternal(isolate,
175 new V8ExternalStringFromScriptData(s));
178 // Converts an ASCII string literal to a V8 string.
179 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate,
180 const char* ascii) {
181 DCHECK(base::IsStringASCII(ascii));
182 size_t length = strlen(ascii);
183 if (length <= kMaxStringBytesForCopy)
184 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString,
185 length);
186 return v8::String::NewExternal(isolate,
187 new V8ExternalASCIILiteral(ascii, length));
190 // Stringizes a V8 object by calling its toString() method. Returns true
191 // on success. This may fail if the toString() throws an exception.
192 bool V8ObjectToUTF16String(v8::Local<v8::Value> object,
193 base::string16* utf16_result,
194 v8::Isolate* isolate) {
195 if (object.IsEmpty())
196 return false;
198 v8::HandleScope scope(isolate);
199 v8::Local<v8::String> str_object = object->ToString(isolate);
200 if (str_object.IsEmpty())
201 return false;
202 *utf16_result = V8StringToUTF16(str_object);
203 return true;
206 // Extracts an hostname argument from |args|. On success returns true
207 // and fills |*hostname| with the result.
208 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
209 std::string* hostname) {
210 // The first argument should be a string.
211 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
212 return false;
214 const base::string16 hostname_utf16 =
215 V8StringToUTF16(v8::Local<v8::String>::Cast(args[0]));
217 // If the hostname is already in ASCII, simply return it as is.
218 if (base::IsStringASCII(hostname_utf16)) {
219 *hostname = base::UTF16ToASCII(hostname_utf16);
220 return true;
223 // Otherwise try to convert it from IDN to punycode.
224 const int kInitialBufferSize = 256;
225 url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output;
226 if (!url::IDNToASCII(hostname_utf16.data(), hostname_utf16.length(),
227 &punycode_output)) {
228 return false;
231 // |punycode_output| should now be ASCII; convert it to a std::string.
232 // (We could use UTF16ToASCII() instead, but that requires an extra string
233 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
234 bool success = base::UTF16ToUTF8(punycode_output.data(),
235 punycode_output.length(),
236 hostname);
237 DCHECK(success);
238 DCHECK(base::IsStringASCII(*hostname));
239 return success;
242 // Wrapper for passing around IP address strings and IPAddressNumber objects.
243 struct IPAddress {
244 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
245 : string_value(ip_string),
246 ip_address_number(ip_number) {
249 // Used for sorting IP addresses in ascending order in SortIpAddressList().
250 // IP6 addresses are placed ahead of IPv4 addresses.
251 bool operator<(const IPAddress& rhs) const {
252 const IPAddressNumber& ip1 = this->ip_address_number;
253 const IPAddressNumber& ip2 = rhs.ip_address_number;
254 if (ip1.size() != ip2.size())
255 return ip1.size() > ip2.size(); // IPv6 before IPv4.
256 DCHECK(ip1.size() == ip2.size());
257 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
260 std::string string_value;
261 IPAddressNumber ip_address_number;
264 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
265 // semi-colon delimited string containing IP addresses.
266 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
267 // IP addresses or an empty string if unable to sort the IP address list.
268 // Returns 'true' if the sorting was successful, and 'false' if the input was an
269 // empty string, a string of separators (";" in this case), or if any of the IP
270 // addresses in the input list failed to parse.
271 bool SortIpAddressList(const std::string& ip_address_list,
272 std::string* sorted_ip_address_list) {
273 sorted_ip_address_list->clear();
275 // Strip all whitespace (mimics IE behavior).
276 std::string cleaned_ip_address_list;
277 base::RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
278 if (cleaned_ip_address_list.empty())
279 return false;
281 // Split-up IP addresses and store them in a vector.
282 std::vector<IPAddress> ip_vector;
283 IPAddressNumber ip_num;
284 base::StringTokenizer str_tok(cleaned_ip_address_list, ";");
285 while (str_tok.GetNext()) {
286 if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
287 return false;
288 ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
291 if (ip_vector.empty()) // Can happen if we have something like
292 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
294 DCHECK(!ip_vector.empty());
296 // Sort lists according to ascending numeric value.
297 if (ip_vector.size() > 1)
298 std::stable_sort(ip_vector.begin(), ip_vector.end());
300 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
301 // IPv4).
302 for (size_t i = 0; i < ip_vector.size(); ++i) {
303 if (i > 0)
304 *sorted_ip_address_list += ";";
305 *sorted_ip_address_list += ip_vector[i].string_value;
307 return true;
310 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
311 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
312 // slash-delimited IP prefix with the top 'n' bits specified in the bit
313 // field. This returns 'true' if the address is in the same subnet, and
314 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
315 // format, or if an address and prefix of different types are used (e.g. IPv6
316 // address and IPv4 prefix).
317 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
318 IPAddressNumber address;
319 if (!ParseIPLiteralToNumber(ip_address, &address))
320 return false;
322 IPAddressNumber prefix;
323 size_t prefix_length_in_bits;
324 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
325 return false;
327 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
328 if (address.size() != prefix.size())
329 return false;
331 DCHECK((address.size() == 4 && prefix.size() == 4) ||
332 (address.size() == 16 && prefix.size() == 16));
334 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
337 // Consider only single component domains like 'foo' as plain host names.
338 bool IsPlainHostName(const std::string& hostname_utf8) {
339 if (hostname_utf8.find('.') != std::string::npos)
340 return false;
342 // IPv6 literals might not contain any periods, however are not considered
343 // plain host names.
344 IPAddressNumber unused;
345 return !ParseIPLiteralToNumber(hostname_utf8, &unused);
348 } // namespace
350 // ProxyResolverV8::Context ---------------------------------------------------
352 class ProxyResolverV8::Context {
353 public:
354 Context(ProxyResolverV8* parent, v8::Isolate* isolate)
355 : parent_(parent),
356 isolate_(isolate) {
357 DCHECK(isolate);
360 ~Context() {
361 v8::Locker locked(isolate_);
362 v8::Isolate::Scope isolate_scope(isolate_);
364 v8_this_.Reset();
365 v8_context_.Reset();
368 JSBindings* js_bindings() {
369 return parent_->js_bindings_;
372 int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
373 v8::Locker locked(isolate_);
374 v8::Isolate::Scope isolate_scope(isolate_);
375 v8::HandleScope scope(isolate_);
377 v8::Local<v8::Context> context =
378 v8::Local<v8::Context>::New(isolate_, v8_context_);
379 v8::Context::Scope function_scope(context);
381 v8::Local<v8::Value> function;
382 if (!GetFindProxyForURL(&function)) {
383 js_bindings()->OnError(
384 -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
385 return ERR_PAC_SCRIPT_FAILED;
388 v8::Local<v8::Value> argv[] = {
389 ASCIIStringToV8String(isolate_, query_url.spec()),
390 ASCIIStringToV8String(isolate_, query_url.HostNoBrackets()),
393 v8::TryCatch try_catch;
394 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
395 context->Global(), arraysize(argv), argv);
397 if (try_catch.HasCaught()) {
398 HandleError(try_catch.Message());
399 return ERR_PAC_SCRIPT_FAILED;
402 if (!ret->IsString()) {
403 js_bindings()->OnError(
404 -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
405 return ERR_PAC_SCRIPT_FAILED;
408 base::string16 ret_str = V8StringToUTF16(v8::Local<v8::String>::Cast(ret));
410 if (!base::IsStringASCII(ret_str)) {
411 // TODO(eroman): Rather than failing when a wide string is returned, we
412 // could extend the parsing to handle IDNA hostnames by
413 // converting them to ASCII punycode.
414 // crbug.com/47234
415 base::string16 error_message =
416 base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
417 "(crbug.com/47234): ") + ret_str;
418 js_bindings()->OnError(-1, error_message);
419 return ERR_PAC_SCRIPT_FAILED;
422 results->UsePacString(base::UTF16ToASCII(ret_str));
423 return OK;
426 int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
427 v8::Locker locked(isolate_);
428 v8::Isolate::Scope isolate_scope(isolate_);
429 v8::HandleScope scope(isolate_);
431 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
432 v8::Local<v8::External> v8_this =
433 v8::Local<v8::External>::New(isolate_, v8_this_);
434 v8::Local<v8::ObjectTemplate> global_template =
435 v8::ObjectTemplate::New(isolate_);
437 // Attach the javascript bindings.
438 v8::Local<v8::FunctionTemplate> alert_template =
439 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
440 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"),
441 alert_template);
443 v8::Local<v8::FunctionTemplate> my_ip_address_template =
444 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
445 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
446 my_ip_address_template);
448 v8::Local<v8::FunctionTemplate> dns_resolve_template =
449 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
450 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
451 dns_resolve_template);
453 v8::Local<v8::FunctionTemplate> is_plain_host_name_template =
454 v8::FunctionTemplate::New(isolate_, &IsPlainHostNameCallback, v8_this);
455 global_template->Set(ASCIILiteralToV8String(isolate_, "isPlainHostName"),
456 is_plain_host_name_template);
458 // Microsoft's PAC extensions:
460 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
461 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
462 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
463 dns_resolve_ex_template);
465 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
466 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
467 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
468 my_ip_address_ex_template);
470 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
471 v8::FunctionTemplate::New(isolate_,
472 &SortIpAddressListCallback,
473 v8_this);
474 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
475 sort_ip_address_list_template);
477 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
478 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
479 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
480 is_in_net_ex_template);
482 v8_context_.Reset(
483 isolate_, v8::Context::New(isolate_, NULL, global_template));
485 v8::Local<v8::Context> context =
486 v8::Local<v8::Context>::New(isolate_, v8_context_);
487 v8::Context::Scope ctx(context);
489 // Add the PAC utility functions to the environment.
490 // (This script should never fail, as it is a string literal!)
491 // Note that the two string literals are concatenated.
492 int rv = RunScript(
493 ASCIILiteralToV8String(
494 isolate_,
495 PROXY_RESOLVER_SCRIPT
496 PROXY_RESOLVER_SCRIPT_EX),
497 kPacUtilityResourceName);
498 if (rv != OK) {
499 NOTREACHED();
500 return rv;
503 // Add the user's PAC code to the environment.
504 rv =
505 RunScript(ScriptDataToV8String(isolate_, pac_script), kPacResourceName);
506 if (rv != OK)
507 return rv;
509 // At a minimum, the FindProxyForURL() function must be defined for this
510 // to be a legitimiate PAC script.
511 v8::Local<v8::Value> function;
512 if (!GetFindProxyForURL(&function)) {
513 js_bindings()->OnError(
514 -1, base::ASCIIToUTF16("FindProxyForURL() is undefined."));
515 return ERR_PAC_SCRIPT_FAILED;
518 return OK;
521 private:
522 bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
523 v8::Local<v8::Context> context =
524 v8::Local<v8::Context>::New(isolate_, v8_context_);
525 *function =
526 context->Global()->Get(
527 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
528 return (*function)->IsFunction();
531 // Handle an exception thrown by V8.
532 void HandleError(v8::Local<v8::Message> message) {
533 base::string16 error_message;
534 int line_number = -1;
536 if (!message.IsEmpty()) {
537 line_number = message->GetLineNumber();
538 V8ObjectToUTF16String(message->Get(), &error_message, isolate_);
541 js_bindings()->OnError(line_number, error_message);
544 // Compiles and runs |script| in the current V8 context.
545 // Returns OK on success, otherwise an error code.
546 int RunScript(v8::Local<v8::String> script, const char* script_name) {
547 v8::TryCatch try_catch;
549 // Compile the script.
550 v8::ScriptOrigin origin =
551 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
552 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
554 // Execute.
555 if (!code.IsEmpty())
556 code->Run();
558 // Check for errors.
559 if (try_catch.HasCaught()) {
560 HandleError(try_catch.Message());
561 return ERR_PAC_SCRIPT_FAILED;
564 return OK;
567 // V8 callback for when "alert()" is invoked by the PAC script.
568 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
569 Context* context =
570 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
572 // Like firefox we assume "undefined" if no argument was specified, and
573 // disregard any arguments beyond the first.
574 base::string16 message;
575 if (args.Length() == 0) {
576 message = base::ASCIIToUTF16("undefined");
577 } else {
578 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
579 return; // toString() threw an exception.
582 context->js_bindings()->Alert(message);
585 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
586 static void MyIpAddressCallback(
587 const v8::FunctionCallbackInfo<v8::Value>& args) {
588 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
591 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
592 static void MyIpAddressExCallback(
593 const v8::FunctionCallbackInfo<v8::Value>& args) {
594 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
597 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
598 static void DnsResolveCallback(
599 const v8::FunctionCallbackInfo<v8::Value>& args) {
600 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
603 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
604 static void DnsResolveExCallback(
605 const v8::FunctionCallbackInfo<v8::Value>& args) {
606 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
609 // Shared code for implementing:
610 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
611 static void DnsResolveCallbackHelper(
612 const v8::FunctionCallbackInfo<v8::Value>& args,
613 JSBindings::ResolveDnsOperation op) {
614 Context* context =
615 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
617 std::string hostname;
619 // dnsResolve() and dnsResolveEx() need at least 1 argument.
620 if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
621 if (!GetHostnameArgument(args, &hostname)) {
622 if (op == JSBindings::DNS_RESOLVE)
623 args.GetReturnValue().SetNull();
624 return;
628 std::string result;
629 bool success;
630 bool terminate = false;
633 v8::Unlocker unlocker(args.GetIsolate());
634 success = context->js_bindings()->ResolveDns(
635 hostname, op, &result, &terminate);
638 if (terminate)
639 v8::V8::TerminateExecution(args.GetIsolate());
641 if (success) {
642 args.GetReturnValue().Set(
643 ASCIIStringToV8String(args.GetIsolate(), result));
644 return;
647 // Each function handles resolution errors differently.
648 switch (op) {
649 case JSBindings::DNS_RESOLVE:
650 args.GetReturnValue().SetNull();
651 return;
652 case JSBindings::DNS_RESOLVE_EX:
653 args.GetReturnValue().SetEmptyString();
654 return;
655 case JSBindings::MY_IP_ADDRESS:
656 args.GetReturnValue().Set(
657 ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
658 return;
659 case JSBindings::MY_IP_ADDRESS_EX:
660 args.GetReturnValue().SetEmptyString();
661 return;
664 NOTREACHED();
667 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
668 static void SortIpAddressListCallback(
669 const v8::FunctionCallbackInfo<v8::Value>& args) {
670 // We need at least one string argument.
671 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
672 args.GetReturnValue().SetNull();
673 return;
676 std::string ip_address_list =
677 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
678 if (!base::IsStringASCII(ip_address_list)) {
679 args.GetReturnValue().SetNull();
680 return;
682 std::string sorted_ip_address_list;
683 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
684 if (!success) {
685 args.GetReturnValue().Set(false);
686 return;
688 args.GetReturnValue().Set(
689 ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
692 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
693 static void IsInNetExCallback(
694 const v8::FunctionCallbackInfo<v8::Value>& args) {
695 // We need at least 2 string arguments.
696 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
697 args[1].IsEmpty() || !args[1]->IsString()) {
698 args.GetReturnValue().SetNull();
699 return;
702 std::string ip_address =
703 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
704 if (!base::IsStringASCII(ip_address)) {
705 args.GetReturnValue().Set(false);
706 return;
708 std::string ip_prefix =
709 V8StringToUTF8(v8::Local<v8::String>::Cast(args[1]));
710 if (!base::IsStringASCII(ip_prefix)) {
711 args.GetReturnValue().Set(false);
712 return;
714 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
717 // V8 callback for when "isPlainHostName()" is invoked by the PAC script.
718 static void IsPlainHostNameCallback(
719 const v8::FunctionCallbackInfo<v8::Value>& args) {
720 // Need at least 1 string arguments.
721 if (args.Length() < 1 || args[0].IsEmpty() || !args[0]->IsString()) {
722 args.GetIsolate()->ThrowException(
723 v8::Exception::TypeError(ASCIIStringToV8String(
724 args.GetIsolate(), "Requires 1 string parameter")));
725 return;
728 std::string hostname_utf8 =
729 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
730 args.GetReturnValue().Set(IsPlainHostName(hostname_utf8));
733 mutable base::Lock lock_;
734 ProxyResolverV8* parent_;
735 v8::Isolate* isolate_;
736 v8::Persistent<v8::External> v8_this_;
737 v8::Persistent<v8::Context> v8_context_;
740 // ProxyResolverV8 ------------------------------------------------------------
742 ProxyResolverV8::ProxyResolverV8()
743 : ProxyResolver(true /*expects_pac_bytes*/),
744 js_bindings_(NULL) {
747 ProxyResolverV8::~ProxyResolverV8() {}
749 int ProxyResolverV8::GetProxyForURL(
750 const GURL& query_url, ProxyInfo* results,
751 const CompletionCallback& /*callback*/,
752 RequestHandle* /*request*/,
753 const BoundNetLog& net_log) {
754 DCHECK(js_bindings_);
756 // If the V8 instance has not been initialized (either because
757 // SetPacScript() wasn't called yet, or because it failed.
758 if (!context_)
759 return ERR_FAILED;
761 // Otherwise call into V8.
762 int rv = context_->ResolveProxy(query_url, results);
764 return rv;
767 void ProxyResolverV8::CancelRequest(RequestHandle request) {
768 // This is a synchronous ProxyResolver; no possibility for async requests.
769 NOTREACHED();
772 LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
773 NOTREACHED();
774 return LOAD_STATE_IDLE;
777 void ProxyResolverV8::CancelSetPacScript() {
778 NOTREACHED();
781 int ProxyResolverV8::SetPacScript(
782 const scoped_refptr<ProxyResolverScriptData>& script_data,
783 const CompletionCallback& /*callback*/) {
784 DCHECK(script_data.get());
785 DCHECK(js_bindings_);
787 context_.reset();
788 if (script_data->utf16().empty())
789 return ERR_PAC_SCRIPT_FAILED;
791 // Try parsing the PAC script.
792 scoped_ptr<Context> context(new Context(this, GetDefaultIsolate()));
793 int rv = context->InitV8(script_data);
794 if (rv == OK)
795 context_.reset(context.release());
796 return rv;
799 // static
800 void ProxyResolverV8::EnsureIsolateCreated() {
801 if (g_proxy_resolver_isolate_)
802 return;
803 gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode,
804 gin::ArrayBufferAllocator::SharedInstance());
805 g_proxy_resolver_isolate_ = new gin::IsolateHolder;
806 ANNOTATE_LEAKING_OBJECT_PTR(g_proxy_resolver_isolate_);
809 // static
810 v8::Isolate* ProxyResolverV8::GetDefaultIsolate() {
811 DCHECK(g_proxy_resolver_isolate_)
812 << "Must call ProxyResolverV8::EnsureIsolateCreated() first";
813 return g_proxy_resolver_isolate_->isolate();
816 gin::IsolateHolder* ProxyResolverV8::g_proxy_resolver_isolate_ = NULL;
818 // static
819 size_t ProxyResolverV8::GetTotalHeapSize() {
820 if (!g_proxy_resolver_isolate_)
821 return 0;
823 v8::Locker locked(g_proxy_resolver_isolate_->isolate());
824 v8::Isolate::Scope isolate_scope(g_proxy_resolver_isolate_->isolate());
825 v8::HeapStatistics heap_statistics;
826 g_proxy_resolver_isolate_->isolate()->GetHeapStatistics(&heap_statistics);
827 return heap_statistics.total_heap_size();
830 // static
831 size_t ProxyResolverV8::GetUsedHeapSize() {
832 if (!g_proxy_resolver_isolate_)
833 return 0;
835 v8::Locker locked(g_proxy_resolver_isolate_->isolate());
836 v8::Isolate::Scope isolate_scope(g_proxy_resolver_isolate_->isolate());
837 v8::HeapStatistics heap_statistics;
838 g_proxy_resolver_isolate_->isolate()->GetHeapStatistics(&heap_statistics);
839 return heap_statistics.used_heap_size();
842 } // namespace net