Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8.cc
blobc41b1906fe94ce452f7f67551a3d299533afaab3
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/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/synchronization/lock.h"
19 #include "gin/array_buffer.h"
20 #include "gin/public/isolate_holder.h"
21 #include "gin/v8_initializer.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/net_util.h"
24 #include "net/log/net_log.h"
25 #include "net/proxy/proxy_info.h"
26 #include "net/proxy/proxy_resolver_script.h"
27 #include "url/gurl.h"
28 #include "url/url_canon.h"
29 #include "v8/include/v8.h"
31 // Notes on the javascript environment:
33 // For the majority of the PAC utility functions, we use the same code
34 // as Firefox. See the javascript library that proxy_resolver_scipt.h
35 // pulls in.
37 // In addition, we implement a subset of Microsoft's extensions to PAC.
38 // - myIpAddressEx()
39 // - dnsResolveEx()
40 // - isResolvableEx()
41 // - isInNetEx()
42 // - sortIpAddressList()
44 // It is worth noting that the original PAC specification does not describe
45 // the return values on failure. Consequently, there are compatibility
46 // differences between browsers on what to return on failure, which are
47 // illustrated below:
49 // --------------------+-------------+-------------------+--------------
50 // | Firefox3 | InternetExplorer8 | --> Us <---
51 // --------------------+-------------+-------------------+--------------
52 // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
53 // dnsResolve() | null | false | null
54 // myIpAddressEx() | N/A | "" | ""
55 // sortIpAddressList() | N/A | false | false
56 // dnsResolveEx() | N/A | "" | ""
57 // isInNetEx() | N/A | false | false
58 // --------------------+-------------+-------------------+--------------
60 // TODO(eroman): The cell above reading ??? means I didn't test it.
62 // Another difference is in how dnsResolve() and myIpAddress() are
63 // implemented -- whether they should restrict to IPv4 results, or
64 // include both IPv4 and IPv6. The following table illustrates the
65 // differences:
67 // --------------------+-------------+-------------------+--------------
68 // | Firefox3 | InternetExplorer8 | --> Us <---
69 // --------------------+-------------+-------------------+--------------
70 // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
71 // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
72 // isResolvable() | IPv4/IPv6 | IPv4 | IPv4
73 // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
74 // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
75 // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
76 // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
77 // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
78 // -----------------+-------------+-------------------+--------------
80 namespace net {
82 namespace {
84 // Pseudo-name for the PAC script.
85 const char kPacResourceName[] = "proxy-pac-script.js";
86 // Pseudo-name for the PAC utility script.
87 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
89 // External string wrapper so V8 can access the UTF16 string wrapped by
90 // ProxyResolverScriptData.
91 class V8ExternalStringFromScriptData
92 : public v8::String::ExternalStringResource {
93 public:
94 explicit V8ExternalStringFromScriptData(
95 const scoped_refptr<ProxyResolverScriptData>& script_data)
96 : script_data_(script_data) {}
98 const uint16_t* data() const override {
99 return reinterpret_cast<const uint16*>(script_data_->utf16().data());
102 size_t length() const override { 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
111 : public v8::String::ExternalOneByteStringResource {
112 public:
113 // |ascii| must be a NULL-terminated C string, and must remain valid
114 // throughout this object's lifetime.
115 V8ExternalASCIILiteral(const char* ascii, size_t length)
116 : ascii_(ascii), length_(length) {
117 DCHECK(base::IsStringASCII(ascii));
120 const char* data() const override { return ascii_; }
122 size_t length() const override { 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::Local<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::Local<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(v8::Isolate* isolate,
159 const std::string& s) {
160 DCHECK(base::IsStringASCII(s));
161 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString,
162 s.size());
165 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
166 // V8 string.
167 v8::Local<v8::String> ScriptDataToV8String(
168 v8::Isolate* isolate, const scoped_refptr<ProxyResolverScriptData>& s) {
169 if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
170 return v8::String::NewFromTwoByte(
171 isolate,
172 reinterpret_cast<const uint16_t*>(s->utf16().data()),
173 v8::String::kNormalString,
174 s->utf16().size());
176 return v8::String::NewExternal(isolate,
177 new V8ExternalStringFromScriptData(s));
180 // Converts an ASCII string literal to a V8 string.
181 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate,
182 const char* ascii) {
183 DCHECK(base::IsStringASCII(ascii));
184 size_t length = strlen(ascii);
185 if (length <= kMaxStringBytesForCopy)
186 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString,
187 length);
188 return v8::String::NewExternal(isolate,
189 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())
198 return false;
200 v8::HandleScope scope(isolate);
201 v8::Local<v8::String> str_object = object->ToString(isolate);
202 if (str_object.IsEmpty())
203 return false;
204 *utf16_result = V8StringToUTF16(str_object);
205 return true;
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())
214 return false;
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);
222 return true;
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(),
229 &punycode_output)) {
230 return false;
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(),
238 hostname);
239 DCHECK(success);
240 DCHECK(base::IsStringASCII(*hostname));
241 return success;
244 // Wrapper for passing around IP address strings and IPAddressNumber objects.
245 struct IPAddress {
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())
281 return false;
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))
289 return false;
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
303 // IPv4).
304 for (size_t i = 0; i < ip_vector.size(); ++i) {
305 if (i > 0)
306 *sorted_ip_address_list += ";";
307 *sorted_ip_address_list += ip_vector[i].string_value;
309 return true;
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))
322 return false;
324 IPAddressNumber prefix;
325 size_t prefix_length_in_bits;
326 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
327 return false;
329 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
330 if (address.size() != prefix.size())
331 return false;
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)
342 return false;
344 // IPv6 literals might not contain any periods, however are not considered
345 // plain host names.
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 {
358 public:
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_);
365 if (!holder_) {
366 // Do one-time initialization for V8.
367 if (!has_initialized_v8_) {
368 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
369 gin::V8Initializer::LoadV8Snapshot();
370 #endif
372 gin::IsolateHolder::Initialize(
373 gin::IsolateHolder::kNonStrictMode,
374 gin::ArrayBufferAllocator::SharedInstance());
376 has_initialized_v8_ = true;
379 holder_.reset(new gin::IsolateHolder);
382 return holder_->isolate();
385 v8::Isolate* GetSharedIsolateWithoutCreating() {
386 base::AutoLock l(lock_);
387 return holder_ ? holder_->isolate() : NULL;
390 private:
391 base::Lock lock_;
392 scoped_ptr<gin::IsolateHolder> holder_;
393 bool has_initialized_v8_;
395 DISALLOW_COPY_AND_ASSIGN(SharedIsolateFactory);
398 base::LazyInstance<SharedIsolateFactory>::Leaky g_isolate_factory =
399 LAZY_INSTANCE_INITIALIZER;
401 } // namespace
403 // ProxyResolverV8::Context ---------------------------------------------------
405 class ProxyResolverV8::Context {
406 public:
407 Context(ProxyResolverV8* parent, v8::Isolate* isolate)
408 : parent_(parent),
409 isolate_(isolate) {
410 DCHECK(isolate);
413 ~Context() {
414 v8::Locker locked(isolate_);
415 v8::Isolate::Scope isolate_scope(isolate_);
417 v8_this_.Reset();
418 v8_context_.Reset();
421 JSBindings* js_bindings() {
422 return parent_->js_bindings_;
425 int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
426 v8::Locker locked(isolate_);
427 v8::Isolate::Scope isolate_scope(isolate_);
428 v8::HandleScope scope(isolate_);
430 v8::Local<v8::Context> context =
431 v8::Local<v8::Context>::New(isolate_, v8_context_);
432 v8::Context::Scope function_scope(context);
434 v8::Local<v8::Value> function;
435 int rv = GetFindProxyForURL(&function);
436 if (rv != OK)
437 return rv;
439 v8::Local<v8::Value> argv[] = {
440 ASCIIStringToV8String(isolate_, query_url.spec()),
441 ASCIIStringToV8String(isolate_, query_url.HostNoBrackets()),
444 v8::TryCatch try_catch;
445 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
446 context->Global(), arraysize(argv), argv);
448 if (try_catch.HasCaught()) {
449 HandleError(try_catch.Message());
450 return ERR_PAC_SCRIPT_FAILED;
453 if (!ret->IsString()) {
454 js_bindings()->OnError(
455 -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
456 return ERR_PAC_SCRIPT_FAILED;
459 base::string16 ret_str = V8StringToUTF16(v8::Local<v8::String>::Cast(ret));
461 if (!base::IsStringASCII(ret_str)) {
462 // TODO(eroman): Rather than failing when a wide string is returned, we
463 // could extend the parsing to handle IDNA hostnames by
464 // converting them to ASCII punycode.
465 // crbug.com/47234
466 base::string16 error_message =
467 base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
468 "(crbug.com/47234): ") + ret_str;
469 js_bindings()->OnError(-1, error_message);
470 return ERR_PAC_SCRIPT_FAILED;
473 results->UsePacString(base::UTF16ToASCII(ret_str));
474 return OK;
477 int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
478 v8::Locker locked(isolate_);
479 v8::Isolate::Scope isolate_scope(isolate_);
480 v8::HandleScope scope(isolate_);
482 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
483 v8::Local<v8::External> v8_this =
484 v8::Local<v8::External>::New(isolate_, v8_this_);
485 v8::Local<v8::ObjectTemplate> global_template =
486 v8::ObjectTemplate::New(isolate_);
488 // Attach the javascript bindings.
489 v8::Local<v8::FunctionTemplate> alert_template =
490 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
491 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"),
492 alert_template);
494 v8::Local<v8::FunctionTemplate> my_ip_address_template =
495 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
496 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
497 my_ip_address_template);
499 v8::Local<v8::FunctionTemplate> dns_resolve_template =
500 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
501 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
502 dns_resolve_template);
504 v8::Local<v8::FunctionTemplate> is_plain_host_name_template =
505 v8::FunctionTemplate::New(isolate_, &IsPlainHostNameCallback, v8_this);
506 global_template->Set(ASCIILiteralToV8String(isolate_, "isPlainHostName"),
507 is_plain_host_name_template);
509 // Microsoft's PAC extensions:
511 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
512 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
513 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
514 dns_resolve_ex_template);
516 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
517 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
518 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
519 my_ip_address_ex_template);
521 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
522 v8::FunctionTemplate::New(isolate_,
523 &SortIpAddressListCallback,
524 v8_this);
525 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
526 sort_ip_address_list_template);
528 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
529 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
530 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
531 is_in_net_ex_template);
533 v8_context_.Reset(
534 isolate_, v8::Context::New(isolate_, NULL, global_template));
536 v8::Local<v8::Context> context =
537 v8::Local<v8::Context>::New(isolate_, v8_context_);
538 v8::Context::Scope ctx(context);
540 // Add the PAC utility functions to the environment.
541 // (This script should never fail, as it is a string literal!)
542 // Note that the two string literals are concatenated.
543 int rv = RunScript(
544 ASCIILiteralToV8String(
545 isolate_,
546 PROXY_RESOLVER_SCRIPT
547 PROXY_RESOLVER_SCRIPT_EX),
548 kPacUtilityResourceName);
549 if (rv != OK) {
550 NOTREACHED();
551 return rv;
554 // Add the user's PAC code to the environment.
555 rv =
556 RunScript(ScriptDataToV8String(isolate_, pac_script), kPacResourceName);
557 if (rv != OK)
558 return rv;
560 // At a minimum, the FindProxyForURL() function must be defined for this
561 // to be a legitimiate PAC script.
562 v8::Local<v8::Value> function;
563 return GetFindProxyForURL(&function);
566 private:
567 int GetFindProxyForURL(v8::Local<v8::Value>* function) {
568 v8::Local<v8::Context> context =
569 v8::Local<v8::Context>::New(isolate_, v8_context_);
571 v8::TryCatch try_catch;
573 *function =
574 context->Global()->Get(
575 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
577 if (try_catch.HasCaught())
578 HandleError(try_catch.Message());
580 // The value should only be empty if an exception was thrown. Code
581 // defensively just in case.
582 DCHECK_EQ(function->IsEmpty(), try_catch.HasCaught());
583 if (function->IsEmpty() || try_catch.HasCaught()) {
584 js_bindings()->OnError(
586 base::ASCIIToUTF16("Accessing FindProxyForURL threw an exception."));
587 return ERR_PAC_SCRIPT_FAILED;
590 if (!(*function)->IsFunction()) {
591 js_bindings()->OnError(
592 -1, base::ASCIIToUTF16(
593 "FindProxyForURL is undefined or not a function."));
594 return ERR_PAC_SCRIPT_FAILED;
597 return OK;
600 // Handle an exception thrown by V8.
601 void HandleError(v8::Local<v8::Message> message) {
602 base::string16 error_message;
603 int line_number = -1;
605 if (!message.IsEmpty()) {
606 line_number = message->GetLineNumber();
607 V8ObjectToUTF16String(message->Get(), &error_message, isolate_);
610 js_bindings()->OnError(line_number, error_message);
613 // Compiles and runs |script| in the current V8 context.
614 // Returns OK on success, otherwise an error code.
615 int RunScript(v8::Local<v8::String> script, const char* script_name) {
616 v8::TryCatch try_catch;
618 // Compile the script.
619 v8::ScriptOrigin origin =
620 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
621 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
623 // Execute.
624 if (!code.IsEmpty())
625 code->Run();
627 // Check for errors.
628 if (try_catch.HasCaught()) {
629 HandleError(try_catch.Message());
630 return ERR_PAC_SCRIPT_FAILED;
633 return OK;
636 // V8 callback for when "alert()" is invoked by the PAC script.
637 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
638 Context* context =
639 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
641 // Like firefox we assume "undefined" if no argument was specified, and
642 // disregard any arguments beyond the first.
643 base::string16 message;
644 if (args.Length() == 0) {
645 message = base::ASCIIToUTF16("undefined");
646 } else {
647 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
648 return; // toString() threw an exception.
651 context->js_bindings()->Alert(message);
654 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
655 static void MyIpAddressCallback(
656 const v8::FunctionCallbackInfo<v8::Value>& args) {
657 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
660 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
661 static void MyIpAddressExCallback(
662 const v8::FunctionCallbackInfo<v8::Value>& args) {
663 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
666 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
667 static void DnsResolveCallback(
668 const v8::FunctionCallbackInfo<v8::Value>& args) {
669 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
672 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
673 static void DnsResolveExCallback(
674 const v8::FunctionCallbackInfo<v8::Value>& args) {
675 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
678 // Shared code for implementing:
679 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
680 static void DnsResolveCallbackHelper(
681 const v8::FunctionCallbackInfo<v8::Value>& args,
682 JSBindings::ResolveDnsOperation op) {
683 Context* context =
684 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
686 std::string hostname;
688 // dnsResolve() and dnsResolveEx() need at least 1 argument.
689 if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
690 if (!GetHostnameArgument(args, &hostname)) {
691 if (op == JSBindings::DNS_RESOLVE)
692 args.GetReturnValue().SetNull();
693 return;
697 std::string result;
698 bool success;
699 bool terminate = false;
702 v8::Unlocker unlocker(args.GetIsolate());
703 success = context->js_bindings()->ResolveDns(
704 hostname, op, &result, &terminate);
707 if (terminate)
708 v8::V8::TerminateExecution(args.GetIsolate());
710 if (success) {
711 args.GetReturnValue().Set(
712 ASCIIStringToV8String(args.GetIsolate(), result));
713 return;
716 // Each function handles resolution errors differently.
717 switch (op) {
718 case JSBindings::DNS_RESOLVE:
719 args.GetReturnValue().SetNull();
720 return;
721 case JSBindings::DNS_RESOLVE_EX:
722 args.GetReturnValue().SetEmptyString();
723 return;
724 case JSBindings::MY_IP_ADDRESS:
725 args.GetReturnValue().Set(
726 ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
727 return;
728 case JSBindings::MY_IP_ADDRESS_EX:
729 args.GetReturnValue().SetEmptyString();
730 return;
733 NOTREACHED();
736 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
737 static void SortIpAddressListCallback(
738 const v8::FunctionCallbackInfo<v8::Value>& args) {
739 // We need at least one string argument.
740 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
741 args.GetReturnValue().SetNull();
742 return;
745 std::string ip_address_list =
746 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
747 if (!base::IsStringASCII(ip_address_list)) {
748 args.GetReturnValue().SetNull();
749 return;
751 std::string sorted_ip_address_list;
752 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
753 if (!success) {
754 args.GetReturnValue().Set(false);
755 return;
757 args.GetReturnValue().Set(
758 ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
761 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
762 static void IsInNetExCallback(
763 const v8::FunctionCallbackInfo<v8::Value>& args) {
764 // We need at least 2 string arguments.
765 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
766 args[1].IsEmpty() || !args[1]->IsString()) {
767 args.GetReturnValue().SetNull();
768 return;
771 std::string ip_address =
772 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
773 if (!base::IsStringASCII(ip_address)) {
774 args.GetReturnValue().Set(false);
775 return;
777 std::string ip_prefix =
778 V8StringToUTF8(v8::Local<v8::String>::Cast(args[1]));
779 if (!base::IsStringASCII(ip_prefix)) {
780 args.GetReturnValue().Set(false);
781 return;
783 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
786 // V8 callback for when "isPlainHostName()" is invoked by the PAC script.
787 static void IsPlainHostNameCallback(
788 const v8::FunctionCallbackInfo<v8::Value>& args) {
789 // Need at least 1 string arguments.
790 if (args.Length() < 1 || args[0].IsEmpty() || !args[0]->IsString()) {
791 args.GetIsolate()->ThrowException(
792 v8::Exception::TypeError(ASCIIStringToV8String(
793 args.GetIsolate(), "Requires 1 string parameter")));
794 return;
797 std::string hostname_utf8 =
798 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
799 args.GetReturnValue().Set(IsPlainHostName(hostname_utf8));
802 mutable base::Lock lock_;
803 ProxyResolverV8* parent_;
804 v8::Isolate* isolate_;
805 v8::Persistent<v8::External> v8_this_;
806 v8::Persistent<v8::Context> v8_context_;
809 // ProxyResolverV8 ------------------------------------------------------------
811 ProxyResolverV8::ProxyResolverV8()
812 : ProxyResolver(true /*expects_pac_bytes*/),
813 js_bindings_(NULL) {
816 ProxyResolverV8::~ProxyResolverV8() {}
818 int ProxyResolverV8::GetProxyForURL(
819 const GURL& query_url, ProxyInfo* results,
820 const CompletionCallback& /*callback*/,
821 RequestHandle* /*request*/,
822 const BoundNetLog& net_log) {
823 DCHECK(js_bindings_);
825 // If the V8 instance has not been initialized (either because
826 // SetPacScript() wasn't called yet, or because it failed.
827 if (!context_)
828 return ERR_FAILED;
830 // Otherwise call into V8.
831 int rv = context_->ResolveProxy(query_url, results);
833 return rv;
836 void ProxyResolverV8::CancelRequest(RequestHandle request) {
837 // This is a synchronous ProxyResolver; no possibility for async requests.
838 NOTREACHED();
841 LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
842 NOTREACHED();
843 return LOAD_STATE_IDLE;
846 void ProxyResolverV8::CancelSetPacScript() {
847 NOTREACHED();
850 int ProxyResolverV8::SetPacScript(
851 const scoped_refptr<ProxyResolverScriptData>& script_data,
852 const CompletionCallback& /*callback*/) {
853 DCHECK(script_data.get());
854 DCHECK(js_bindings_);
856 context_.reset();
857 if (script_data->utf16().empty())
858 return ERR_PAC_SCRIPT_FAILED;
860 // Try parsing the PAC script.
861 scoped_ptr<Context> context(
862 new Context(this, g_isolate_factory.Get().GetSharedIsolate()));
863 int rv = context->InitV8(script_data);
864 if (rv == OK)
865 context_.reset(context.release());
866 return rv;
869 // static
870 size_t ProxyResolverV8::GetTotalHeapSize() {
871 v8::Isolate* isolate =
872 g_isolate_factory.Get().GetSharedIsolateWithoutCreating();
873 if (!isolate)
874 return 0;
876 v8::Locker locked(isolate);
877 v8::Isolate::Scope isolate_scope(isolate);
878 v8::HeapStatistics heap_statistics;
879 isolate->GetHeapStatistics(&heap_statistics);
880 return heap_statistics.total_heap_size();
883 // static
884 size_t ProxyResolverV8::GetUsedHeapSize() {
885 v8::Isolate* isolate =
886 g_isolate_factory.Get().GetSharedIsolateWithoutCreating();
887 if (!isolate)
888 return 0;
890 v8::Locker locked(isolate);
891 v8::Isolate::Scope isolate_scope(isolate);
892 v8::HeapStatistics heap_statistics;
893 isolate->GetHeapStatistics(&heap_statistics);
894 return heap_statistics.used_heap_size();
897 } // namespace net