Move android_app_version_* into an inner variables dict.
[chromium-blink-merge.git] / net / proxy / proxy_resolver_v8.cc
blob57d68b5d8f9af105ff66644307665e74b9dffe31
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 "net/base/net_errors.h"
22 #include "net/base/net_log.h"
23 #include "net/base/net_util.h"
24 #include "net/proxy/proxy_info.h"
25 #include "net/proxy/proxy_resolver_script.h"
26 #include "url/gurl.h"
27 #include "url/url_canon.h"
28 #include "v8/include/v8.h"
30 // Notes on the javascript environment:
32 // For the majority of the PAC utility functions, we use the same code
33 // as Firefox. See the javascript library that proxy_resolver_scipt.h
34 // pulls in.
36 // In addition, we implement a subset of Microsoft's extensions to PAC.
37 // - myIpAddressEx()
38 // - dnsResolveEx()
39 // - isResolvableEx()
40 // - isInNetEx()
41 // - sortIpAddressList()
43 // It is worth noting that the original PAC specification does not describe
44 // the return values on failure. Consequently, there are compatibility
45 // differences between browsers on what to return on failure, which are
46 // illustrated below:
48 // --------------------+-------------+-------------------+--------------
49 // | Firefox3 | InternetExplorer8 | --> Us <---
50 // --------------------+-------------+-------------------+--------------
51 // myIpAddress() | "127.0.0.1" | ??? | "127.0.0.1"
52 // dnsResolve() | null | false | null
53 // myIpAddressEx() | N/A | "" | ""
54 // sortIpAddressList() | N/A | false | false
55 // dnsResolveEx() | N/A | "" | ""
56 // isInNetEx() | N/A | false | false
57 // --------------------+-------------+-------------------+--------------
59 // TODO(eroman): The cell above reading ??? means I didn't test it.
61 // Another difference is in how dnsResolve() and myIpAddress() are
62 // implemented -- whether they should restrict to IPv4 results, or
63 // include both IPv4 and IPv6. The following table illustrates the
64 // differences:
66 // --------------------+-------------+-------------------+--------------
67 // | Firefox3 | InternetExplorer8 | --> Us <---
68 // --------------------+-------------+-------------------+--------------
69 // myIpAddress() | IPv4/IPv6 | IPv4 | IPv4
70 // dnsResolve() | IPv4/IPv6 | IPv4 | IPv4
71 // isResolvable() | IPv4/IPv6 | IPv4 | IPv4
72 // myIpAddressEx() | N/A | IPv4/IPv6 | IPv4/IPv6
73 // dnsResolveEx() | N/A | IPv4/IPv6 | IPv4/IPv6
74 // sortIpAddressList() | N/A | IPv4/IPv6 | IPv4/IPv6
75 // isResolvableEx() | N/A | IPv4/IPv6 | IPv4/IPv6
76 // isInNetEx() | N/A | IPv4/IPv6 | IPv4/IPv6
77 // -----------------+-------------+-------------------+--------------
79 namespace net {
81 namespace {
83 // Pseudo-name for the PAC script.
84 const char kPacResourceName[] = "proxy-pac-script.js";
85 // Pseudo-name for the PAC utility script.
86 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
88 // External string wrapper so V8 can access the UTF16 string wrapped by
89 // ProxyResolverScriptData.
90 class V8ExternalStringFromScriptData
91 : public v8::String::ExternalStringResource {
92 public:
93 explicit V8ExternalStringFromScriptData(
94 const scoped_refptr<ProxyResolverScriptData>& script_data)
95 : script_data_(script_data) {}
97 const uint16_t* data() const override {
98 return reinterpret_cast<const uint16*>(script_data_->utf16().data());
101 size_t length() const override { return script_data_->utf16().size(); }
103 private:
104 const scoped_refptr<ProxyResolverScriptData> script_data_;
105 DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
108 // External string wrapper so V8 can access a string literal.
109 class V8ExternalASCIILiteral
110 : public v8::String::ExternalOneByteStringResource {
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 const char* data() const override { return ascii_; }
121 size_t length() const override { return length_; }
123 private:
124 const char* ascii_;
125 size_t length_;
126 DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
129 // When creating a v8::String from a C++ string we have two choices: create
130 // a copy, or create a wrapper that shares the same underlying storage.
131 // For small strings it is better to just make a copy, whereas for large
132 // strings there are savings by sharing the storage. This number identifies
133 // the cutoff length for when to start wrapping rather than creating copies.
134 const size_t kMaxStringBytesForCopy = 256;
136 // Converts a V8 String to a UTF8 std::string.
137 std::string V8StringToUTF8(v8::Local<v8::String> s) {
138 int len = s->Length();
139 std::string result;
140 if (len > 0)
141 s->WriteUtf8(WriteInto(&result, len + 1));
142 return result;
145 // Converts a V8 String to a UTF16 base::string16.
146 base::string16 V8StringToUTF16(v8::Local<v8::String> s) {
147 int len = s->Length();
148 base::string16 result;
149 // Note that the reinterpret cast is because on Windows string16 is an alias
150 // to wstring, and hence has character type wchar_t not uint16_t.
151 if (len > 0)
152 s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
153 return result;
156 // Converts an ASCII std::string to a V8 string.
157 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate,
158 const std::string& s) {
159 DCHECK(base::IsStringASCII(s));
160 return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString,
161 s.size());
164 // Converts a UTF16 base::string16 (warpped by a ProxyResolverScriptData) to a
165 // V8 string.
166 v8::Local<v8::String> ScriptDataToV8String(
167 v8::Isolate* isolate, const scoped_refptr<ProxyResolverScriptData>& s) {
168 if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
169 return v8::String::NewFromTwoByte(
170 isolate,
171 reinterpret_cast<const uint16_t*>(s->utf16().data()),
172 v8::String::kNormalString,
173 s->utf16().size());
175 return v8::String::NewExternal(isolate,
176 new V8ExternalStringFromScriptData(s));
179 // Converts an ASCII string literal to a V8 string.
180 v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate,
181 const char* ascii) {
182 DCHECK(base::IsStringASCII(ascii));
183 size_t length = strlen(ascii);
184 if (length <= kMaxStringBytesForCopy)
185 return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString,
186 length);
187 return v8::String::NewExternal(isolate,
188 new V8ExternalASCIILiteral(ascii, length));
191 // Stringizes a V8 object by calling its toString() method. Returns true
192 // on success. This may fail if the toString() throws an exception.
193 bool V8ObjectToUTF16String(v8::Local<v8::Value> object,
194 base::string16* utf16_result,
195 v8::Isolate* isolate) {
196 if (object.IsEmpty())
197 return false;
199 v8::HandleScope scope(isolate);
200 v8::Local<v8::String> str_object = object->ToString(isolate);
201 if (str_object.IsEmpty())
202 return false;
203 *utf16_result = V8StringToUTF16(str_object);
204 return true;
207 // Extracts an hostname argument from |args|. On success returns true
208 // and fills |*hostname| with the result.
209 bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args,
210 std::string* hostname) {
211 // The first argument should be a string.
212 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
213 return false;
215 const base::string16 hostname_utf16 =
216 V8StringToUTF16(v8::Local<v8::String>::Cast(args[0]));
218 // If the hostname is already in ASCII, simply return it as is.
219 if (base::IsStringASCII(hostname_utf16)) {
220 *hostname = base::UTF16ToASCII(hostname_utf16);
221 return true;
224 // Otherwise try to convert it from IDN to punycode.
225 const int kInitialBufferSize = 256;
226 url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output;
227 if (!url::IDNToASCII(hostname_utf16.data(), hostname_utf16.length(),
228 &punycode_output)) {
229 return false;
232 // |punycode_output| should now be ASCII; convert it to a std::string.
233 // (We could use UTF16ToASCII() instead, but that requires an extra string
234 // copy. Since ASCII is a subset of UTF8 the following is equivalent).
235 bool success = base::UTF16ToUTF8(punycode_output.data(),
236 punycode_output.length(),
237 hostname);
238 DCHECK(success);
239 DCHECK(base::IsStringASCII(*hostname));
240 return success;
243 // Wrapper for passing around IP address strings and IPAddressNumber objects.
244 struct IPAddress {
245 IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
246 : string_value(ip_string),
247 ip_address_number(ip_number) {
250 // Used for sorting IP addresses in ascending order in SortIpAddressList().
251 // IP6 addresses are placed ahead of IPv4 addresses.
252 bool operator<(const IPAddress& rhs) const {
253 const IPAddressNumber& ip1 = this->ip_address_number;
254 const IPAddressNumber& ip2 = rhs.ip_address_number;
255 if (ip1.size() != ip2.size())
256 return ip1.size() > ip2.size(); // IPv6 before IPv4.
257 DCHECK(ip1.size() == ip2.size());
258 return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0; // Ascending order.
261 std::string string_value;
262 IPAddressNumber ip_address_number;
265 // Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
266 // semi-colon delimited string containing IP addresses.
267 // |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
268 // IP addresses or an empty string if unable to sort the IP address list.
269 // Returns 'true' if the sorting was successful, and 'false' if the input was an
270 // empty string, a string of separators (";" in this case), or if any of the IP
271 // addresses in the input list failed to parse.
272 bool SortIpAddressList(const std::string& ip_address_list,
273 std::string* sorted_ip_address_list) {
274 sorted_ip_address_list->clear();
276 // Strip all whitespace (mimics IE behavior).
277 std::string cleaned_ip_address_list;
278 base::RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
279 if (cleaned_ip_address_list.empty())
280 return false;
282 // Split-up IP addresses and store them in a vector.
283 std::vector<IPAddress> ip_vector;
284 IPAddressNumber ip_num;
285 base::StringTokenizer str_tok(cleaned_ip_address_list, ";");
286 while (str_tok.GetNext()) {
287 if (!ParseIPLiteralToNumber(str_tok.token(), &ip_num))
288 return false;
289 ip_vector.push_back(IPAddress(str_tok.token(), ip_num));
292 if (ip_vector.empty()) // Can happen if we have something like
293 return false; // sortIpAddressList(";") or sortIpAddressList("; ;")
295 DCHECK(!ip_vector.empty());
297 // Sort lists according to ascending numeric value.
298 if (ip_vector.size() > 1)
299 std::stable_sort(ip_vector.begin(), ip_vector.end());
301 // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
302 // IPv4).
303 for (size_t i = 0; i < ip_vector.size(); ++i) {
304 if (i > 0)
305 *sorted_ip_address_list += ";";
306 *sorted_ip_address_list += ip_vector[i].string_value;
308 return true;
311 // Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
312 // containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
313 // slash-delimited IP prefix with the top 'n' bits specified in the bit
314 // field. This returns 'true' if the address is in the same subnet, and
315 // 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
316 // format, or if an address and prefix of different types are used (e.g. IPv6
317 // address and IPv4 prefix).
318 bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
319 IPAddressNumber address;
320 if (!ParseIPLiteralToNumber(ip_address, &address))
321 return false;
323 IPAddressNumber prefix;
324 size_t prefix_length_in_bits;
325 if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
326 return false;
328 // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
329 if (address.size() != prefix.size())
330 return false;
332 DCHECK((address.size() == 4 && prefix.size() == 4) ||
333 (address.size() == 16 && prefix.size() == 16));
335 return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
338 // Consider only single component domains like 'foo' as plain host names.
339 bool IsPlainHostName(const std::string& hostname_utf8) {
340 if (hostname_utf8.find('.') != std::string::npos)
341 return false;
343 // IPv6 literals might not contain any periods, however are not considered
344 // plain host names.
345 IPAddressNumber unused;
346 return !ParseIPLiteralToNumber(hostname_utf8, &unused);
349 // All instances of ProxyResolverV8 share the same v8::Isolate. This isolate is
350 // created lazily the first time it is needed and lives until process shutdown.
351 // This creation might happen from any thread, as ProxyResolverV8 is typically
352 // run in a threadpool.
354 // TODO(eroman): The lazily created isolate is never freed. Instead it should be
355 // disposed once there are no longer any ProxyResolverV8 referencing it.
356 class SharedIsolateFactory {
357 public:
358 SharedIsolateFactory() : has_initialized_v8_(false) {}
360 // Lazily creates a v8::Isolate, or returns the already created instance.
361 v8::Isolate* GetSharedIsolate() {
362 base::AutoLock l(lock_);
364 if (!holder_) {
365 // Do one-time initialization for V8.
366 if (!has_initialized_v8_) {
367 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
368 gin::IsolateHolder::LoadV8Snapshot();
369 #endif
371 gin::IsolateHolder::Initialize(
372 gin::IsolateHolder::kNonStrictMode,
373 gin::ArrayBufferAllocator::SharedInstance());
375 has_initialized_v8_ = true;
378 holder_.reset(new gin::IsolateHolder);
381 return holder_->isolate();
384 v8::Isolate* GetSharedIsolateWithoutCreating() {
385 base::AutoLock l(lock_);
386 return holder_ ? holder_->isolate() : NULL;
389 private:
390 base::Lock lock_;
391 scoped_ptr<gin::IsolateHolder> holder_;
392 bool has_initialized_v8_;
394 DISALLOW_COPY_AND_ASSIGN(SharedIsolateFactory);
397 base::LazyInstance<SharedIsolateFactory>::Leaky g_isolate_factory =
398 LAZY_INSTANCE_INITIALIZER;
400 } // namespace
402 // ProxyResolverV8::Context ---------------------------------------------------
404 class ProxyResolverV8::Context {
405 public:
406 Context(ProxyResolverV8* parent, v8::Isolate* isolate)
407 : parent_(parent),
408 isolate_(isolate) {
409 DCHECK(isolate);
412 ~Context() {
413 v8::Locker locked(isolate_);
414 v8::Isolate::Scope isolate_scope(isolate_);
416 v8_this_.Reset();
417 v8_context_.Reset();
420 JSBindings* js_bindings() {
421 return parent_->js_bindings_;
424 int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
425 v8::Locker locked(isolate_);
426 v8::Isolate::Scope isolate_scope(isolate_);
427 v8::HandleScope scope(isolate_);
429 v8::Local<v8::Context> context =
430 v8::Local<v8::Context>::New(isolate_, v8_context_);
431 v8::Context::Scope function_scope(context);
433 v8::Local<v8::Value> function;
434 int rv = GetFindProxyForURL(&function);
435 if (rv != OK)
436 return rv;
438 v8::Local<v8::Value> argv[] = {
439 ASCIIStringToV8String(isolate_, query_url.spec()),
440 ASCIIStringToV8String(isolate_, query_url.HostNoBrackets()),
443 v8::TryCatch try_catch;
444 v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
445 context->Global(), arraysize(argv), argv);
447 if (try_catch.HasCaught()) {
448 HandleError(try_catch.Message());
449 return ERR_PAC_SCRIPT_FAILED;
452 if (!ret->IsString()) {
453 js_bindings()->OnError(
454 -1, base::ASCIIToUTF16("FindProxyForURL() did not return a string."));
455 return ERR_PAC_SCRIPT_FAILED;
458 base::string16 ret_str = V8StringToUTF16(v8::Local<v8::String>::Cast(ret));
460 if (!base::IsStringASCII(ret_str)) {
461 // TODO(eroman): Rather than failing when a wide string is returned, we
462 // could extend the parsing to handle IDNA hostnames by
463 // converting them to ASCII punycode.
464 // crbug.com/47234
465 base::string16 error_message =
466 base::ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
467 "(crbug.com/47234): ") + ret_str;
468 js_bindings()->OnError(-1, error_message);
469 return ERR_PAC_SCRIPT_FAILED;
472 results->UsePacString(base::UTF16ToASCII(ret_str));
473 return OK;
476 int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
477 v8::Locker locked(isolate_);
478 v8::Isolate::Scope isolate_scope(isolate_);
479 v8::HandleScope scope(isolate_);
481 v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
482 v8::Local<v8::External> v8_this =
483 v8::Local<v8::External>::New(isolate_, v8_this_);
484 v8::Local<v8::ObjectTemplate> global_template =
485 v8::ObjectTemplate::New(isolate_);
487 // Attach the javascript bindings.
488 v8::Local<v8::FunctionTemplate> alert_template =
489 v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
490 global_template->Set(ASCIILiteralToV8String(isolate_, "alert"),
491 alert_template);
493 v8::Local<v8::FunctionTemplate> my_ip_address_template =
494 v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
495 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
496 my_ip_address_template);
498 v8::Local<v8::FunctionTemplate> dns_resolve_template =
499 v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
500 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
501 dns_resolve_template);
503 v8::Local<v8::FunctionTemplate> is_plain_host_name_template =
504 v8::FunctionTemplate::New(isolate_, &IsPlainHostNameCallback, v8_this);
505 global_template->Set(ASCIILiteralToV8String(isolate_, "isPlainHostName"),
506 is_plain_host_name_template);
508 // Microsoft's PAC extensions:
510 v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
511 v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
512 global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
513 dns_resolve_ex_template);
515 v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
516 v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
517 global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
518 my_ip_address_ex_template);
520 v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
521 v8::FunctionTemplate::New(isolate_,
522 &SortIpAddressListCallback,
523 v8_this);
524 global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
525 sort_ip_address_list_template);
527 v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
528 v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
529 global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
530 is_in_net_ex_template);
532 v8_context_.Reset(
533 isolate_, v8::Context::New(isolate_, NULL, global_template));
535 v8::Local<v8::Context> context =
536 v8::Local<v8::Context>::New(isolate_, v8_context_);
537 v8::Context::Scope ctx(context);
539 // Add the PAC utility functions to the environment.
540 // (This script should never fail, as it is a string literal!)
541 // Note that the two string literals are concatenated.
542 int rv = RunScript(
543 ASCIILiteralToV8String(
544 isolate_,
545 PROXY_RESOLVER_SCRIPT
546 PROXY_RESOLVER_SCRIPT_EX),
547 kPacUtilityResourceName);
548 if (rv != OK) {
549 NOTREACHED();
550 return rv;
553 // Add the user's PAC code to the environment.
554 rv =
555 RunScript(ScriptDataToV8String(isolate_, pac_script), kPacResourceName);
556 if (rv != OK)
557 return rv;
559 // At a minimum, the FindProxyForURL() function must be defined for this
560 // to be a legitimiate PAC script.
561 v8::Local<v8::Value> function;
562 return GetFindProxyForURL(&function);
565 private:
566 int GetFindProxyForURL(v8::Local<v8::Value>* function) {
567 v8::Local<v8::Context> context =
568 v8::Local<v8::Context>::New(isolate_, v8_context_);
570 v8::TryCatch try_catch;
572 *function =
573 context->Global()->Get(
574 ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
576 if (try_catch.HasCaught())
577 HandleError(try_catch.Message());
579 // The value should only be empty if an exception was thrown. Code
580 // defensively just in case.
581 DCHECK_EQ(function->IsEmpty(), try_catch.HasCaught());
582 if (function->IsEmpty() || try_catch.HasCaught()) {
583 js_bindings()->OnError(
585 base::ASCIIToUTF16("Accessing FindProxyForURL threw an exception."));
586 return ERR_PAC_SCRIPT_FAILED;
589 if (!(*function)->IsFunction()) {
590 js_bindings()->OnError(
591 -1, base::ASCIIToUTF16(
592 "FindProxyForURL is undefined or not a function."));
593 return ERR_PAC_SCRIPT_FAILED;
596 return OK;
599 // Handle an exception thrown by V8.
600 void HandleError(v8::Local<v8::Message> message) {
601 base::string16 error_message;
602 int line_number = -1;
604 if (!message.IsEmpty()) {
605 line_number = message->GetLineNumber();
606 V8ObjectToUTF16String(message->Get(), &error_message, isolate_);
609 js_bindings()->OnError(line_number, error_message);
612 // Compiles and runs |script| in the current V8 context.
613 // Returns OK on success, otherwise an error code.
614 int RunScript(v8::Local<v8::String> script, const char* script_name) {
615 v8::TryCatch try_catch;
617 // Compile the script.
618 v8::ScriptOrigin origin =
619 v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
620 v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
622 // Execute.
623 if (!code.IsEmpty())
624 code->Run();
626 // Check for errors.
627 if (try_catch.HasCaught()) {
628 HandleError(try_catch.Message());
629 return ERR_PAC_SCRIPT_FAILED;
632 return OK;
635 // V8 callback for when "alert()" is invoked by the PAC script.
636 static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
637 Context* context =
638 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
640 // Like firefox we assume "undefined" if no argument was specified, and
641 // disregard any arguments beyond the first.
642 base::string16 message;
643 if (args.Length() == 0) {
644 message = base::ASCIIToUTF16("undefined");
645 } else {
646 if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
647 return; // toString() threw an exception.
650 context->js_bindings()->Alert(message);
653 // V8 callback for when "myIpAddress()" is invoked by the PAC script.
654 static void MyIpAddressCallback(
655 const v8::FunctionCallbackInfo<v8::Value>& args) {
656 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS);
659 // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
660 static void MyIpAddressExCallback(
661 const v8::FunctionCallbackInfo<v8::Value>& args) {
662 DnsResolveCallbackHelper(args, JSBindings::MY_IP_ADDRESS_EX);
665 // V8 callback for when "dnsResolve()" is invoked by the PAC script.
666 static void DnsResolveCallback(
667 const v8::FunctionCallbackInfo<v8::Value>& args) {
668 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE);
671 // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
672 static void DnsResolveExCallback(
673 const v8::FunctionCallbackInfo<v8::Value>& args) {
674 DnsResolveCallbackHelper(args, JSBindings::DNS_RESOLVE_EX);
677 // Shared code for implementing:
678 // - myIpAddress(), myIpAddressEx(), dnsResolve(), dnsResolveEx().
679 static void DnsResolveCallbackHelper(
680 const v8::FunctionCallbackInfo<v8::Value>& args,
681 JSBindings::ResolveDnsOperation op) {
682 Context* context =
683 static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
685 std::string hostname;
687 // dnsResolve() and dnsResolveEx() need at least 1 argument.
688 if (op == JSBindings::DNS_RESOLVE || op == JSBindings::DNS_RESOLVE_EX) {
689 if (!GetHostnameArgument(args, &hostname)) {
690 if (op == JSBindings::DNS_RESOLVE)
691 args.GetReturnValue().SetNull();
692 return;
696 std::string result;
697 bool success;
698 bool terminate = false;
701 v8::Unlocker unlocker(args.GetIsolate());
702 success = context->js_bindings()->ResolveDns(
703 hostname, op, &result, &terminate);
706 if (terminate)
707 v8::V8::TerminateExecution(args.GetIsolate());
709 if (success) {
710 args.GetReturnValue().Set(
711 ASCIIStringToV8String(args.GetIsolate(), result));
712 return;
715 // Each function handles resolution errors differently.
716 switch (op) {
717 case JSBindings::DNS_RESOLVE:
718 args.GetReturnValue().SetNull();
719 return;
720 case JSBindings::DNS_RESOLVE_EX:
721 args.GetReturnValue().SetEmptyString();
722 return;
723 case JSBindings::MY_IP_ADDRESS:
724 args.GetReturnValue().Set(
725 ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
726 return;
727 case JSBindings::MY_IP_ADDRESS_EX:
728 args.GetReturnValue().SetEmptyString();
729 return;
732 NOTREACHED();
735 // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
736 static void SortIpAddressListCallback(
737 const v8::FunctionCallbackInfo<v8::Value>& args) {
738 // We need at least one string argument.
739 if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
740 args.GetReturnValue().SetNull();
741 return;
744 std::string ip_address_list =
745 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
746 if (!base::IsStringASCII(ip_address_list)) {
747 args.GetReturnValue().SetNull();
748 return;
750 std::string sorted_ip_address_list;
751 bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
752 if (!success) {
753 args.GetReturnValue().Set(false);
754 return;
756 args.GetReturnValue().Set(
757 ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
760 // V8 callback for when "isInNetEx()" is invoked by the PAC script.
761 static void IsInNetExCallback(
762 const v8::FunctionCallbackInfo<v8::Value>& args) {
763 // We need at least 2 string arguments.
764 if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
765 args[1].IsEmpty() || !args[1]->IsString()) {
766 args.GetReturnValue().SetNull();
767 return;
770 std::string ip_address =
771 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
772 if (!base::IsStringASCII(ip_address)) {
773 args.GetReturnValue().Set(false);
774 return;
776 std::string ip_prefix =
777 V8StringToUTF8(v8::Local<v8::String>::Cast(args[1]));
778 if (!base::IsStringASCII(ip_prefix)) {
779 args.GetReturnValue().Set(false);
780 return;
782 args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
785 // V8 callback for when "isPlainHostName()" is invoked by the PAC script.
786 static void IsPlainHostNameCallback(
787 const v8::FunctionCallbackInfo<v8::Value>& args) {
788 // Need at least 1 string arguments.
789 if (args.Length() < 1 || args[0].IsEmpty() || !args[0]->IsString()) {
790 args.GetIsolate()->ThrowException(
791 v8::Exception::TypeError(ASCIIStringToV8String(
792 args.GetIsolate(), "Requires 1 string parameter")));
793 return;
796 std::string hostname_utf8 =
797 V8StringToUTF8(v8::Local<v8::String>::Cast(args[0]));
798 args.GetReturnValue().Set(IsPlainHostName(hostname_utf8));
801 mutable base::Lock lock_;
802 ProxyResolverV8* parent_;
803 v8::Isolate* isolate_;
804 v8::Persistent<v8::External> v8_this_;
805 v8::Persistent<v8::Context> v8_context_;
808 // ProxyResolverV8 ------------------------------------------------------------
810 ProxyResolverV8::ProxyResolverV8()
811 : ProxyResolver(true /*expects_pac_bytes*/),
812 js_bindings_(NULL) {
815 ProxyResolverV8::~ProxyResolverV8() {}
817 int ProxyResolverV8::GetProxyForURL(
818 const GURL& query_url, ProxyInfo* results,
819 const CompletionCallback& /*callback*/,
820 RequestHandle* /*request*/,
821 const BoundNetLog& net_log) {
822 DCHECK(js_bindings_);
824 // If the V8 instance has not been initialized (either because
825 // SetPacScript() wasn't called yet, or because it failed.
826 if (!context_)
827 return ERR_FAILED;
829 // Otherwise call into V8.
830 int rv = context_->ResolveProxy(query_url, results);
832 return rv;
835 void ProxyResolverV8::CancelRequest(RequestHandle request) {
836 // This is a synchronous ProxyResolver; no possibility for async requests.
837 NOTREACHED();
840 LoadState ProxyResolverV8::GetLoadState(RequestHandle request) const {
841 NOTREACHED();
842 return LOAD_STATE_IDLE;
845 void ProxyResolverV8::CancelSetPacScript() {
846 NOTREACHED();
849 int ProxyResolverV8::SetPacScript(
850 const scoped_refptr<ProxyResolverScriptData>& script_data,
851 const CompletionCallback& /*callback*/) {
852 DCHECK(script_data.get());
853 DCHECK(js_bindings_);
855 context_.reset();
856 if (script_data->utf16().empty())
857 return ERR_PAC_SCRIPT_FAILED;
859 // Try parsing the PAC script.
860 scoped_ptr<Context> context(
861 new Context(this, g_isolate_factory.Get().GetSharedIsolate()));
862 int rv = context->InitV8(script_data);
863 if (rv == OK)
864 context_.reset(context.release());
865 return rv;
868 // static
869 size_t ProxyResolverV8::GetTotalHeapSize() {
870 v8::Isolate* isolate =
871 g_isolate_factory.Get().GetSharedIsolateWithoutCreating();
872 if (!isolate)
873 return 0;
875 v8::Locker locked(isolate);
876 v8::Isolate::Scope isolate_scope(isolate);
877 v8::HeapStatistics heap_statistics;
878 isolate->GetHeapStatistics(&heap_statistics);
879 return heap_statistics.total_heap_size();
882 // static
883 size_t ProxyResolverV8::GetUsedHeapSize() {
884 v8::Isolate* isolate =
885 g_isolate_factory.Get().GetSharedIsolateWithoutCreating();
886 if (!isolate)
887 return 0;
889 v8::Locker locked(isolate);
890 v8::Isolate::Scope isolate_scope(isolate);
891 v8::HeapStatistics heap_statistics;
892 isolate->GetHeapStatistics(&heap_statistics);
893 return heap_statistics.used_heap_size();
896 } // namespace net