proxy_resolver_v8.cc revision 9fd69be73a96299c7658aa72fc02c4cca4c7b3ea
1// Copyright (c) 2010 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.
4
5#include <algorithm>
6#include <cstdio>
7#include <string>
8
9#include <utils/String16.h>
10
11#include "proxy_resolver_v8.h"
12
13#include "proxy_resolver_script.h"
14#include "net_util.h"
15#include <include/v8.h>
16#include <algorithm>
17#include <vector>
18
19#include <iostream>
20
21#include <string.h>
22#include <utils/String8.h>
23#include <utils/String16.h>
24
25// Notes on the javascript environment:
26//
27// For the majority of the PAC utility functions, we use the same code
28// as Firefox. See the javascript library that proxy_resolver_scipt.h
29// pulls in.
30//
31// In addition, we implement a subset of Microsoft's extensions to PAC.
32// - myIpAddressEx()
33// - dnsResolveEx()
34// - isResolvableEx()
35// - isInNetEx()
36// - sortIpAddressList()
37//
38// It is worth noting that the original PAC specification does not describe
39// the return values on failure. Consequently, there are compatibility
40// differences between browsers on what to return on failure, which are
41// illustrated below:
42//
43// --------------------+-------------+-------------------+--------------
44//                     | Firefox3    | InternetExplorer8 |  --> Us <---
45// --------------------+-------------+-------------------+--------------
46// myIpAddress()       | "127.0.0.1" |  ???              |  "127.0.0.1"
47// dnsResolve()        | null        |  false            |  null
48// myIpAddressEx()     | N/A         |  ""               |  ""
49// sortIpAddressList() | N/A         |  false            |  false
50// dnsResolveEx()      | N/A         |  ""               |  ""
51// isInNetEx()         | N/A         |  false            |  false
52// --------------------+-------------+-------------------+--------------
53//
54// TODO: The cell above reading ??? means I didn't test it.
55//
56// Another difference is in how dnsResolve() and myIpAddress() are
57// implemented -- whether they should restrict to IPv4 results, or
58// include both IPv4 and IPv6. The following table illustrates the
59// differences:
60//
61// --------------------+-------------+-------------------+--------------
62//                     | Firefox3    | InternetExplorer8 |  --> Us <---
63// --------------------+-------------+-------------------+--------------
64// myIpAddress()       | IPv4/IPv6   |  IPv4             |  IPv4
65// dnsResolve()        | IPv4/IPv6   |  IPv4             |  IPv4
66// isResolvable()      | IPv4/IPv6   |  IPv4             |  IPv4
67// myIpAddressEx()     | N/A         |  IPv4/IPv6        |  IPv4/IPv6
68// dnsResolveEx()      | N/A         |  IPv4/IPv6        |  IPv4/IPv6
69// sortIpAddressList() | N/A         |  IPv4/IPv6        |  IPv4/IPv6
70// isResolvableEx()    | N/A         |  IPv4/IPv6        |  IPv4/IPv6
71// isInNetEx()         | N/A         |  IPv4/IPv6        |  IPv4/IPv6
72// -----------------+-------------+-------------------+--------------
73
74static bool DoIsStringASCII(const android::String16& str) {
75  for (size_t i = 0; i < str.size(); i++) {
76    unsigned short c = str.string()[i];
77    if (c > 0x7F)
78      return false;
79  }
80  return true;
81}
82
83bool IsStringASCII(const android::String16& str) {
84  return DoIsStringASCII(str);
85}
86
87namespace net {
88
89namespace {
90
91// Pseudo-name for the PAC script.
92const char kPacResourceName[] = "proxy-pac-script.js";
93// Pseudo-name for the PAC utility script.
94const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
95
96// External string wrapper so V8 can access the UTF16 string wrapped by
97// ProxyResolverScriptData.
98class V8ExternalStringFromScriptData
99    : public v8::String::ExternalStringResource {
100 public:
101  explicit V8ExternalStringFromScriptData(
102      const android::String16& script_data)
103      : script_data_(script_data) {}
104
105  virtual const uint16_t* data() const {
106    return script_data_.string();
107  }
108
109  virtual size_t length() const {
110    return script_data_.size();
111  }
112
113 private:
114  const android::String16& script_data_;
115//  DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
116};
117
118// External string wrapper so V8 can access a string literal.
119class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
120 public:
121  // |ascii| must be a NULL-terminated C string, and must remain valid
122  // throughout this object's lifetime.
123  V8ExternalASCIILiteral(const char* ascii, size_t length)
124      : ascii_(ascii), length_(length) {
125  }
126
127  virtual const char* data() const {
128    return ascii_;
129  }
130
131  virtual size_t length() const {
132    return length_;
133  }
134
135 private:
136  const char* ascii_;
137  size_t length_;
138};
139
140// When creating a v8::String from a C++ string we have two choices: create
141// a copy, or create a wrapper that shares the same underlying storage.
142// For small strings it is better to just make a copy, whereas for large
143// strings there are savings by sharing the storage. This number identifies
144// the cutoff length for when to start wrapping rather than creating copies.
145const size_t kMaxStringBytesForCopy = 256;
146
147template <class string_type>
148inline typename string_type::value_type* WriteInto(string_type* str,
149                                                   size_t length_with_null) {
150  str->reserve(length_with_null);
151  str->resize(length_with_null - 1);
152  return &((*str)[0]);
153}
154
155// Converts a V8 String to a UTF8 std::string.
156std::string V8StringToUTF8(v8::Handle<v8::String> s) {
157  std::string result;
158  s->WriteUtf8(WriteInto(&result, s->Length() + 1));
159  return result;
160}
161
162// Converts a V8 String to a UTF16 string.
163android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
164  int len = s->Length();
165  char16_t* buf = new char16_t[len + 1];
166  s->Write(buf, 0, len);
167  android::String16 ret(buf, len);
168  delete buf;
169  return ret;
170}
171
172std::string UTF16ToASCII(const android::String16& str) {
173  android::String8 rstr(str);
174  return std::string(rstr.string());
175}
176
177// Converts an ASCII std::string to a V8 string.
178v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
179  return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
180}
181
182v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
183  return v8::String::NewFromTwoByte(isolate, s.string(), v8::String::kNormalString, s.size());
184}
185
186// Converts an ASCII string literal to a V8 string.
187v8::Local<v8::String> ASCIILiteralToV8String(v8::Isolate* isolate, const char* ascii) {
188//  DCHECK(IsStringASCII(ascii));
189  size_t length = strlen(ascii);
190  if (length <= kMaxStringBytesForCopy)
191    return v8::String::NewFromUtf8(isolate, ascii, v8::String::kNormalString, length);
192  return v8::String::NewExternal(isolate, new V8ExternalASCIILiteral(ascii, length));
193}
194
195// Stringizes a V8 object by calling its toString() method. Returns true
196// on success. This may fail if the toString() throws an exception.
197bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
198                           android::String16* utf16_result,
199                           v8::Isolate* isolate) {
200  if (object.IsEmpty())
201    return false;
202
203  v8::HandleScope scope(isolate);
204  v8::Local<v8::String> str_object = object->ToString();
205  if (str_object.IsEmpty())
206    return false;
207  *utf16_result = V8StringToUTF16(str_object);
208  return true;
209}
210
211// Extracts an hostname argument from |args|. On success returns true
212// and fills |*hostname| with the result.
213bool GetHostnameArgument(const v8::FunctionCallbackInfo<v8::Value>& args, std::string* hostname) {
214  // The first argument should be a string.
215  if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
216    return false;
217
218  const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
219
220  // If the hostname is already in ASCII, simply return it as is.
221  if (IsStringASCII(hostname_utf16)) {
222    *hostname = UTF16ToASCII(hostname_utf16);
223    return true;
224  }
225  return false;
226}
227
228// Wrapper for passing around IP address strings and IPAddressNumber objects.
229struct IPAddress {
230  IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
231      : string_value(ip_string),
232        ip_address_number(ip_number) {
233  }
234
235  // Used for sorting IP addresses in ascending order in SortIpAddressList().
236  // IP6 addresses are placed ahead of IPv4 addresses.
237  bool operator<(const IPAddress& rhs) const {
238    const IPAddressNumber& ip1 = this->ip_address_number;
239    const IPAddressNumber& ip2 = rhs.ip_address_number;
240    if (ip1.size() != ip2.size())
241      return ip1.size() > ip2.size();  // IPv6 before IPv4.
242    return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;  // Ascending order.
243  }
244
245  std::string string_value;
246  IPAddressNumber ip_address_number;
247};
248
249template<typename STR>
250bool RemoveCharsT(const STR& input,
251                  const typename STR::value_type remove_chars[],
252                  STR* output) {
253  bool removed = false;
254  size_t found;
255
256  *output = input;
257
258  found = output->find_first_of(remove_chars);
259  while (found != STR::npos) {
260    removed = true;
261    output->replace(found, 1, STR());
262    found = output->find_first_of(remove_chars, found);
263  }
264
265  return removed;
266}
267
268bool RemoveChars(const std::string& input,
269                 const char remove_chars[],
270                 std::string* output) {
271  return RemoveCharsT(input, remove_chars, output);
272}
273
274// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
275// semi-colon delimited string containing IP addresses.
276// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
277// IP addresses or an empty string if unable to sort the IP address list.
278// Returns 'true' if the sorting was successful, and 'false' if the input was an
279// empty string, a string of separators (";" in this case), or if any of the IP
280// addresses in the input list failed to parse.
281bool SortIpAddressList(const std::string& ip_address_list,
282                       std::string* sorted_ip_address_list) {
283  sorted_ip_address_list->clear();
284
285  // Strip all whitespace (mimics IE behavior).
286  std::string cleaned_ip_address_list;
287  RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
288  if (cleaned_ip_address_list.empty())
289    return false;
290
291  // Split-up IP addresses and store them in a vector.
292  std::vector<IPAddress> ip_vector;
293  IPAddressNumber ip_num;
294  char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
295  while (tok_list != NULL) {
296    if (!ParseIPLiteralToNumber(tok_list, &ip_num))
297      return false;
298    ip_vector.push_back(IPAddress(tok_list, ip_num));
299    tok_list = strtok(NULL, ";");
300  }
301
302  if (ip_vector.empty())  // Can happen if we have something like
303    return false;         // sortIpAddressList(";") or sortIpAddressList("; ;")
304
305  // Sort lists according to ascending numeric value.
306  if (ip_vector.size() > 1)
307    std::stable_sort(ip_vector.begin(), ip_vector.end());
308
309  // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
310  // IPv4).
311  for (size_t i = 0; i < ip_vector.size(); ++i) {
312    if (i > 0)
313      *sorted_ip_address_list += ";";
314    *sorted_ip_address_list += ip_vector[i].string_value;
315  }
316  return true;
317}
318
319
320// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
321// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
322// slash-delimited IP prefix with the top 'n' bits specified in the bit
323// field. This returns 'true' if the address is in the same subnet, and
324// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
325// format, or if an address and prefix of different types are used (e.g. IPv6
326// address and IPv4 prefix).
327bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
328  IPAddressNumber address;
329  std::string cleaned_ip_address;
330  if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
331    return false;
332  if (!ParseIPLiteralToNumber(ip_address, &address))
333    return false;
334
335  IPAddressNumber prefix;
336  size_t prefix_length_in_bits;
337  if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
338    return false;
339
340  // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
341  if (address.size() != prefix.size())
342    return false;
343
344  return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
345}
346
347}  // namespace
348
349// ProxyResolverV8::Context ---------------------------------------------------
350
351class ProxyResolverV8::Context {
352 public:
353  explicit Context(ProxyResolverJSBindings* js_bindings,
354          ProxyErrorListener* error_listener, v8::Isolate* isolate)
355      : js_bindings_(js_bindings), error_listener_(error_listener), isolate_(isolate) {
356  }
357
358  ~Context() {
359    v8::Locker locked(isolate_);
360    v8::Isolate::Scope isolate_scope(isolate_);
361
362    v8_this_.Reset();
363    v8_context_.Reset();
364  }
365
366  int ResolveProxy(const android::String16 url, const android::String16 host,
367        android::String16* results) {
368    v8::Locker locked(isolate_);
369    v8::Isolate::Scope isolate_scope(isolate_);
370    v8::HandleScope scope(isolate_);
371
372    v8::Local<v8::Context> context =
373        v8::Local<v8::Context>::New(isolate_, v8_context_);
374    v8::Context::Scope function_scope(context);
375
376    v8::Local<v8::Value> function;
377    if (!GetFindProxyForURL(&function)) {
378      error_listener_->ErrorMessage(
379          android::String16("FindProxyForURL() is undefined"));
380      return ERR_PAC_SCRIPT_FAILED;
381    }
382
383    v8::Handle<v8::Value> argv[] = {
384        UTF16StringToV8String(isolate_, url),
385        UTF16StringToV8String(isolate_, host) };
386
387    v8::TryCatch try_catch;
388    v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
389        context->Global(), 2, argv);
390
391    if (try_catch.HasCaught()) {
392      error_listener_->ErrorMessage(
393          V8StringToUTF16(try_catch.Message()->Get()));
394      return ERR_PAC_SCRIPT_FAILED;
395    }
396
397    if (!ret->IsString()) {
398      error_listener_->ErrorMessage(
399          android::String16("FindProxyForURL() did not return a string."));
400      return ERR_PAC_SCRIPT_FAILED;
401    }
402
403    *results = V8StringToUTF16(ret->ToString());
404
405    if (!IsStringASCII(*results)) {
406      // TODO:         Rather than failing when a wide string is returned, we
407      //               could extend the parsing to handle IDNA hostnames by
408      //               converting them to ASCII punycode.
409      //               crbug.com/47234
410      error_listener_->ErrorMessage(
411          android::String16("FindProxyForURL() returned a non-ASCII string"));
412      return ERR_PAC_SCRIPT_FAILED;
413    }
414
415    return OK;
416  }
417
418  int InitV8(const android::String16& pac_script) {
419    v8::Locker locked(isolate_);
420    v8::Isolate::Scope isolate_scope(isolate_);
421    v8::HandleScope scope(isolate_);
422
423    v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
424    v8::Local<v8::External> v8_this =
425        v8::Local<v8::External>::New(isolate_, v8_this_);
426    v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
427
428    // Attach the javascript bindings.
429    v8::Local<v8::FunctionTemplate> alert_template =
430        v8::FunctionTemplate::New(isolate_, &AlertCallback, v8_this);
431    global_template->Set(ASCIILiteralToV8String(isolate_, "alert"), alert_template);
432
433    v8::Local<v8::FunctionTemplate> my_ip_address_template =
434        v8::FunctionTemplate::New(isolate_, &MyIpAddressCallback, v8_this);
435    global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddress"),
436        my_ip_address_template);
437
438    v8::Local<v8::FunctionTemplate> dns_resolve_template =
439        v8::FunctionTemplate::New(isolate_, &DnsResolveCallback, v8_this);
440    global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolve"),
441        dns_resolve_template);
442
443    // Microsoft's PAC extensions:
444
445    v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
446        v8::FunctionTemplate::New(isolate_, &DnsResolveExCallback, v8_this);
447    global_template->Set(ASCIILiteralToV8String(isolate_, "dnsResolveEx"),
448                         dns_resolve_ex_template);
449
450    v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
451        v8::FunctionTemplate::New(isolate_, &MyIpAddressExCallback, v8_this);
452    global_template->Set(ASCIILiteralToV8String(isolate_, "myIpAddressEx"),
453                         my_ip_address_ex_template);
454
455    v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
456        v8::FunctionTemplate::New(isolate_, &SortIpAddressListCallback, v8_this);
457    global_template->Set(ASCIILiteralToV8String(isolate_, "sortIpAddressList"),
458                         sort_ip_address_list_template);
459
460    v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
461        v8::FunctionTemplate::New(isolate_, &IsInNetExCallback, v8_this);
462    global_template->Set(ASCIILiteralToV8String(isolate_, "isInNetEx"),
463                         is_in_net_ex_template);
464
465    v8_context_.Reset(
466        isolate_, v8::Context::New(isolate_, NULL, global_template));
467
468    v8::Local<v8::Context> context =
469        v8::Local<v8::Context>::New(isolate_, v8_context_);
470    v8::Context::Scope ctx(context);
471
472    // Add the PAC utility functions to the environment.
473    // (This script should never fail, as it is a string literal!)
474    // Note that the two string literals are concatenated.
475    int rv = RunScript(
476        ASCIILiteralToV8String(isolate_,
477            PROXY_RESOLVER_SCRIPT
478            PROXY_RESOLVER_SCRIPT_EX),
479        kPacUtilityResourceName);
480    if (rv != OK) {
481      return rv;
482    }
483
484    // Add the user's PAC code to the environment.
485    rv = RunScript(UTF16StringToV8String(isolate_, pac_script), kPacResourceName);
486    if (rv != OK) {
487      return rv;
488    }
489
490    // At a minimum, the FindProxyForURL() function must be defined for this
491    // to be a legitimiate PAC script.
492    v8::Local<v8::Value> function;
493    if (!GetFindProxyForURL(&function))
494      return ERR_PAC_SCRIPT_FAILED;
495
496    return OK;
497  }
498
499  void PurgeMemory() {
500    v8::Locker locked(isolate_);
501    v8::Isolate::Scope isolate_scope(isolate_);
502    v8::V8::LowMemoryNotification();
503  }
504
505 private:
506  bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
507    v8::Local<v8::Context> context =
508        v8::Local<v8::Context>::New(isolate_, v8_context_);
509    *function = context->Global()->Get(
510        ASCIILiteralToV8String(isolate_, "FindProxyForURL"));
511    return (*function)->IsFunction();
512  }
513
514  // Handle an exception thrown by V8.
515  void HandleError(v8::Handle<v8::Message> message) {
516    if (message.IsEmpty())
517      return;
518    error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
519  }
520
521  // Compiles and runs |script| in the current V8 context.
522  // Returns OK on success, otherwise an error code.
523  int RunScript(v8::Handle<v8::String> script, const char* script_name) {
524    v8::TryCatch try_catch;
525
526    // Compile the script.
527    v8::ScriptOrigin origin =
528        v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
529    v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
530
531    // Execute.
532    if (!code.IsEmpty())
533      code->Run();
534
535    // Check for errors.
536    if (try_catch.HasCaught()) {
537      HandleError(try_catch.Message());
538      return ERR_PAC_SCRIPT_FAILED;
539    }
540
541    return OK;
542  }
543
544  // V8 callback for when "alert()" is invoked by the PAC script.
545  static void AlertCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
546    Context* context =
547        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
548
549    // Like firefox we assume "undefined" if no argument was specified, and
550    // disregard any arguments beyond the first.
551    android::String16 message;
552    if (args.Length() == 0) {
553      std::string undef = "undefined";
554      android::String8 undef8(undef.c_str());
555      android::String16 wundef(undef8);
556      message = wundef;
557    } else {
558      if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
559        return;  // toString() threw an exception.
560    }
561
562    context->error_listener_->AlertMessage(message);
563    return;
564  }
565
566  // V8 callback for when "myIpAddress()" is invoked by the PAC script.
567  static void MyIpAddressCallback(
568      const v8::FunctionCallbackInfo<v8::Value>& args) {
569    Context* context =
570        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
571
572    std::string result;
573    bool success;
574
575    {
576      v8::Unlocker unlocker(args.GetIsolate());
577
578      // We shouldn't be called with any arguments, but will not complain if
579      // we are.
580      success = context->js_bindings_->MyIpAddress(&result);
581    }
582
583    if (!success) {
584      args.GetReturnValue().Set(ASCIILiteralToV8String(args.GetIsolate(), "127.0.0.1"));
585    } else {
586      args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), result));
587    }
588  }
589
590  // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
591  static void MyIpAddressExCallback(
592      const v8::FunctionCallbackInfo<v8::Value>& args) {
593    Context* context =
594        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
595
596    std::string ip_address_list;
597    bool success;
598
599    {
600      v8::Unlocker unlocker(args.GetIsolate());
601
602      // We shouldn't be called with any arguments, but will not complain if
603      // we are.
604      success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
605    }
606
607    if (!success)
608      ip_address_list = std::string();
609    args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
610  }
611
612  // V8 callback for when "dnsResolve()" is invoked by the PAC script.
613  static void DnsResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
614    Context* context =
615        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
616
617    // We need at least one string argument.
618    std::string hostname;
619    if (!GetHostnameArgument(args, &hostname)) {
620      return;
621    }
622
623    std::string ip_address;
624    bool success;
625
626    {
627      v8::Unlocker unlocker(args.GetIsolate());
628      success = context->js_bindings_->DnsResolve(hostname, &ip_address);
629    }
630
631    if (success) {
632      args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address));
633    } else {
634      args.GetReturnValue().SetNull();
635    }
636  }
637
638  // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
639  static void DnsResolveExCallback(
640      const v8::FunctionCallbackInfo<v8::Value>& args) {
641    Context* context =
642        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
643
644    // We need at least one string argument.
645    std::string hostname;
646    if (!GetHostnameArgument(args, &hostname)) {
647      args.GetReturnValue().SetNull();
648      return;
649    }
650
651    std::string ip_address_list;
652    bool success;
653
654    {
655      v8::Unlocker unlocker(args.GetIsolate());
656      success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
657    }
658
659    if (!success)
660      ip_address_list = std::string();
661
662    args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), ip_address_list));
663  }
664
665  // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
666  static void SortIpAddressListCallback(
667      const v8::FunctionCallbackInfo<v8::Value>& args) {
668    // We need at least one string argument.
669    if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString()) {
670      args.GetReturnValue().SetNull();
671      return;
672    }
673
674    std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
675    std::string sorted_ip_address_list;
676    bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
677    if (!success) {
678      args.GetReturnValue().Set(false);
679      return;
680    }
681    args.GetReturnValue().Set(ASCIIStringToV8String(args.GetIsolate(), sorted_ip_address_list));
682  }
683
684  // V8 callback for when "isInNetEx()" is invoked by the PAC script.
685  static void IsInNetExCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
686    // We need at least 2 string arguments.
687    if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
688        args[1].IsEmpty() || !args[1]->IsString()) {
689      args.GetReturnValue().SetNull();
690      return;
691    }
692
693    std::string ip_address = V8StringToUTF8(args[0]->ToString());
694    std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
695    args.GetReturnValue().Set(IsInNetEx(ip_address, ip_prefix));
696  }
697
698  ProxyResolverJSBindings* js_bindings_;
699  ProxyErrorListener* error_listener_;
700  v8::Isolate* isolate_;
701  v8::Persistent<v8::External> v8_this_;
702  v8::Persistent<v8::Context> v8_context_;
703};
704
705// ProxyResolverV8 ------------------------------------------------------------
706
707ProxyResolverV8::ProxyResolverV8(
708    ProxyResolverJSBindings* custom_js_bindings,
709    ProxyErrorListener* error_listener)
710    : context_(NULL), js_bindings_(custom_js_bindings),
711      error_listener_(error_listener) {
712
713}
714
715ProxyResolverV8::~ProxyResolverV8() {
716  if (context_ != NULL) {
717    delete context_;
718    context_ = NULL;
719  }
720  if (js_bindings_ != NULL) {
721    delete js_bindings_;
722  }
723}
724
725int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
726                                    android::String16* results) {
727  // If the V8 instance has not been initialized (either because
728  // SetPacScript() wasn't called yet, or because it failed.
729  if (context_ == NULL)
730    return ERR_FAILED;
731
732  // Otherwise call into V8.
733  int rv = context_->ResolveProxy(spec, host, results);
734
735  return rv;
736}
737
738void ProxyResolverV8::PurgeMemory() {
739  context_->PurgeMemory();
740}
741
742int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
743  if (context_ != NULL) {
744    delete context_;
745    context_ = NULL;
746  }
747  if (script_data.size() == 0)
748    return ERR_PAC_SCRIPT_FAILED;
749
750  // Try parsing the PAC script.
751  context_ = new Context(js_bindings_, error_listener_, v8::Isolate::New());
752  int rv;
753  if ((rv = context_->InitV8(script_data)) != OK) {
754    context_ = NULL;
755  }
756  if (rv != OK)
757    context_ = NULL;
758  return rv;
759}
760
761}  // namespace net
762