proxy_resolver_v8.cc revision eafc0fa00299168e43ad7d42c545c18b12aa8f3e
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
177android::String16 ASCIIToUTF16(const std::string str) {
178  android::String8 str8(str.c_str());
179  return android::String16(str8);
180}
181
182// Converts an ASCII std::string to a V8 string.
183v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
184  return v8::String::New(s.data(), s.size());
185}
186
187v8::Local<v8::String> UTF16StringToV8String(const android::String16& s) {
188  return v8::String::New(s.string(), s.size());
189}
190
191// Converts an ASCII string literal to a V8 string.
192v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
193//  DCHECK(IsStringASCII(ascii));
194  size_t length = strlen(ascii);
195  if (length <= kMaxStringBytesForCopy)
196    return v8::String::New(ascii, length);
197  return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
198}
199
200// Stringizes a V8 object by calling its toString() method. Returns true
201// on success. This may fail if the toString() throws an exception.
202bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
203                           android::String16* utf16_result) {
204  if (object.IsEmpty())
205    return false;
206
207  v8::HandleScope scope;
208  v8::Local<v8::String> str_object = object->ToString();
209  if (str_object.IsEmpty())
210    return false;
211  *utf16_result = V8StringToUTF16(str_object);
212  return true;
213}
214
215// Extracts an hostname argument from |args|. On success returns true
216// and fills |*hostname| with the result.
217bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) {
218  // The first argument should be a string.
219  if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
220    return false;
221
222  const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
223
224  // If the hostname is already in ASCII, simply return it as is.
225  if (IsStringASCII(hostname_utf16)) {
226    *hostname = UTF16ToASCII(hostname_utf16);
227    return true;
228  }
229  return false;
230}
231
232// Wrapper for passing around IP address strings and IPAddressNumber objects.
233struct IPAddress {
234  IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
235      : string_value(ip_string),
236        ip_address_number(ip_number) {
237  }
238
239  // Used for sorting IP addresses in ascending order in SortIpAddressList().
240  // IP6 addresses are placed ahead of IPv4 addresses.
241  bool operator<(const IPAddress& rhs) const {
242    const IPAddressNumber& ip1 = this->ip_address_number;
243    const IPAddressNumber& ip2 = rhs.ip_address_number;
244    if (ip1.size() != ip2.size())
245      return ip1.size() > ip2.size();  // IPv6 before IPv4.
246    return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;  // Ascending order.
247  }
248
249  std::string string_value;
250  IPAddressNumber ip_address_number;
251};
252
253template<typename STR>
254bool RemoveCharsT(const STR& input,
255                  const typename STR::value_type remove_chars[],
256                  STR* output) {
257  bool removed = false;
258  size_t found;
259
260  *output = input;
261
262  found = output->find_first_of(remove_chars);
263  while (found != STR::npos) {
264    removed = true;
265    output->replace(found, 1, STR());
266    found = output->find_first_of(remove_chars, found);
267  }
268
269  return removed;
270}
271
272bool RemoveChars(const std::string& input,
273                 const char remove_chars[],
274                 std::string* output) {
275  return RemoveCharsT(input, remove_chars, output);
276}
277
278// Handler for "sortIpAddressList(IpAddressList)". |ip_address_list| is a
279// semi-colon delimited string containing IP addresses.
280// |sorted_ip_address_list| is the resulting list of sorted semi-colon delimited
281// IP addresses or an empty string if unable to sort the IP address list.
282// Returns 'true' if the sorting was successful, and 'false' if the input was an
283// empty string, a string of separators (";" in this case), or if any of the IP
284// addresses in the input list failed to parse.
285bool SortIpAddressList(const std::string& ip_address_list,
286                       std::string* sorted_ip_address_list) {
287  sorted_ip_address_list->clear();
288
289  // Strip all whitespace (mimics IE behavior).
290  std::string cleaned_ip_address_list;
291  RemoveChars(ip_address_list, " \t", &cleaned_ip_address_list);
292  if (cleaned_ip_address_list.empty())
293    return false;
294
295  // Split-up IP addresses and store them in a vector.
296  std::vector<IPAddress> ip_vector;
297  IPAddressNumber ip_num;
298  char *tok_list = strtok((char *)cleaned_ip_address_list.c_str(), ";");
299  while (tok_list != NULL) {
300    if (!ParseIPLiteralToNumber(tok_list, &ip_num))
301      return false;
302    ip_vector.push_back(IPAddress(tok_list, ip_num));
303    tok_list = strtok(tok_list, ";");
304  }
305
306  if (ip_vector.empty())  // Can happen if we have something like
307    return false;         // sortIpAddressList(";") or sortIpAddressList("; ;")
308
309  // Sort lists according to ascending numeric value.
310  if (ip_vector.size() > 1)
311    std::stable_sort(ip_vector.begin(), ip_vector.end());
312
313  // Return a semi-colon delimited list of sorted addresses (IPv6 followed by
314  // IPv4).
315  for (size_t i = 0; i < ip_vector.size(); ++i) {
316    if (i > 0)
317      *sorted_ip_address_list += ";";
318    *sorted_ip_address_list += ip_vector[i].string_value;
319  }
320  return true;
321}
322
323
324// Handler for "isInNetEx(ip_address, ip_prefix)". |ip_address| is a string
325// containing an IPv4/IPv6 address, and |ip_prefix| is a string containg a
326// slash-delimited IP prefix with the top 'n' bits specified in the bit
327// field. This returns 'true' if the address is in the same subnet, and
328// 'false' otherwise. Also returns 'false' if the prefix is in an incorrect
329// format, or if an address and prefix of different types are used (e.g. IPv6
330// address and IPv4 prefix).
331bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
332  IPAddressNumber address;
333  std::string cleaned_ip_address;
334  if (RemoveChars(ip_address, " \t", &cleaned_ip_address))
335    return false;
336  if (!ParseIPLiteralToNumber(ip_address, &address))
337    return false;
338
339  IPAddressNumber prefix;
340  size_t prefix_length_in_bits;
341  if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
342    return false;
343
344  // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
345  if (address.size() != prefix.size())
346    return false;
347
348  return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
349}
350
351}  // namespace
352
353// ProxyResolverV8::Context ---------------------------------------------------
354
355class ProxyResolverV8::Context {
356 public:
357  explicit Context(ProxyResolverJSBindings* js_bindings,
358          ProxyErrorListener* error_listener)
359      : js_bindings_(js_bindings), error_listener_(error_listener) {
360  }
361
362  ~Context() {
363    v8::Locker locked;
364
365    v8_this_.Dispose();
366    v8_context_.Dispose();
367
368    // Run the V8 garbage collector. We do this to be sure the
369    // ExternalStringResource objects we allocated get properly disposed.
370    // Otherwise when running the unit-tests they may get leaked.
371    // See crbug.com/48145.
372    PurgeMemory();
373  }
374
375  int ResolveProxy(const android::String16 url, const android::String16 host, android::String16* results) {
376    v8::Locker locked;
377    v8::HandleScope scope;
378
379    v8::Context::Scope function_scope(v8_context_);
380
381    v8::Local<v8::Value> function;
382    if (!GetFindProxyForURL(&function)) {
383      *results = ASCIIToUTF16("FindProxyForURL() is undefined");
384      return ERR_PAC_SCRIPT_FAILED;
385    }
386
387    v8::Handle<v8::Value> argv[] = {
388        UTF16StringToV8String(url),
389        UTF16StringToV8String(host) };
390
391    v8::TryCatch try_catch;
392    v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
393        v8_context_->Global(), 2, argv);
394
395    if (try_catch.HasCaught()) {
396      *results = V8StringToUTF16(try_catch.Message()->Get());
397      return ERR_PAC_SCRIPT_FAILED;
398    }
399
400    if (!ret->IsString()) {
401      *results = ASCIIToUTF16("FindProxyForURL() did not return a string.");
402      return ERR_PAC_SCRIPT_FAILED;
403    }
404
405    *results = V8StringToUTF16(ret->ToString());
406
407    if (!IsStringASCII(*results)) {
408      // TODO:         Rather than failing when a wide string is returned, we
409      //               could extend the parsing to handle IDNA hostnames by
410      //               converting them to ASCII punycode.
411      //               crbug.com/47234
412      *results = ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string");
413      return ERR_PAC_SCRIPT_FAILED;
414    }
415
416    return OK;
417  }
418
419  int InitV8(const android::String16& pac_script) {
420    v8::Locker locked;
421    v8::HandleScope scope;
422
423    v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
424    v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
425
426    // Attach the javascript bindings.
427    v8::Local<v8::FunctionTemplate> alert_template =
428        v8::FunctionTemplate::New(&AlertCallback, v8_this_);
429    global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
430
431    v8::Local<v8::FunctionTemplate> my_ip_address_template =
432        v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
433    global_template->Set(ASCIILiteralToV8String("myIpAddress"),
434        my_ip_address_template);
435
436    v8::Local<v8::FunctionTemplate> dns_resolve_template =
437        v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
438    global_template->Set(ASCIILiteralToV8String("dnsResolve"),
439        dns_resolve_template);
440
441    // Microsoft's PAC extensions:
442
443    v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
444        v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
445    global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
446                         dns_resolve_ex_template);
447
448    v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
449        v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
450    global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
451                         my_ip_address_ex_template);
452
453    v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
454        v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this_);
455    global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
456                         sort_ip_address_list_template);
457
458    v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
459        v8::FunctionTemplate::New(&IsInNetExCallback, v8_this_);
460    global_template->Set(ASCIILiteralToV8String("isInNetEx"),
461                         is_in_net_ex_template);
462
463    v8_context_ = v8::Context::New(NULL, global_template);
464
465    v8::Context::Scope ctx(v8_context_);
466
467    // Add the PAC utility functions to the environment.
468    // (This script should never fail, as it is a string literal!)
469    // Note that the two string literals are concatenated.
470    int rv = RunScript(
471        ASCIILiteralToV8String(
472            PROXY_RESOLVER_SCRIPT
473            PROXY_RESOLVER_SCRIPT_EX),
474        kPacUtilityResourceName);
475    if (rv != OK) {
476      return rv;
477    }
478
479    // Add the user's PAC code to the environment.
480    rv = RunScript(UTF16StringToV8String(pac_script), kPacResourceName);
481    if (rv != OK) {
482      return rv;
483    }
484
485    // At a minimum, the FindProxyForURL() function must be defined for this
486    // to be a legitimiate PAC script.
487    v8::Local<v8::Value> function;
488    if (!GetFindProxyForURL(&function))
489      return ERR_PAC_SCRIPT_FAILED;
490
491    return OK;
492  }
493
494  void PurgeMemory() {
495    v8::Locker locked;
496    // Repeatedly call the V8 idle notification until it returns true ("nothing
497    // more to free").  Note that it makes more sense to do this than to
498    // implement a new "delete everything" pass because object references make
499    // it difficult to free everything possible in just one pass.
500    while (!v8::V8::IdleNotification())
501      ;
502  }
503
504 private:
505  bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
506    *function = v8_context_->Global()->Get(
507        ASCIILiteralToV8String("FindProxyForURL"));
508    return (*function)->IsFunction();
509  }
510
511  // Handle an exception thrown by V8.
512  void HandleError(v8::Handle<v8::Message> message) {
513    if (message.IsEmpty())
514      return;
515    error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
516  }
517
518  // Compiles and runs |script| in the current V8 context.
519  // Returns OK on success, otherwise an error code.
520  int RunScript(v8::Handle<v8::String> script, const char* script_name) {
521    v8::TryCatch try_catch;
522
523    // Compile the script.
524    v8::ScriptOrigin origin =
525        v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
526    v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
527
528    // Execute.
529    if (!code.IsEmpty())
530      code->Run();
531
532    // Check for errors.
533    if (try_catch.HasCaught()) {
534      HandleError(try_catch.Message());
535      return ERR_PAC_SCRIPT_FAILED;
536    }
537
538    return OK;
539  }
540
541  // V8 callback for when "alert()" is invoked by the PAC script.
542  static v8::Handle<v8::Value> AlertCallback(const v8::Arguments& args) {
543    Context* context =
544        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
545
546    // Like firefox we assume "undefined" if no argument was specified, and
547    // disregard any arguments beyond the first.
548    android::String16 message;
549    if (args.Length() == 0) {
550      std::string undef = "undefined";
551      android::String8 undef8(undef.c_str());
552      android::String16 wundef(undef8);
553      message = wundef;
554    } else {
555      if (!V8ObjectToUTF16String(args[0], &message))
556        return v8::Undefined();  // toString() threw an exception.
557    }
558
559    context->error_listener_->AlertMessage(message);
560    return v8::Undefined();
561  }
562
563  // V8 callback for when "myIpAddress()" is invoked by the PAC script.
564  static v8::Handle<v8::Value> MyIpAddressCallback(const v8::Arguments& args) {
565    Context* context =
566        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
567
568    std::string result;
569    bool success;
570
571    {
572      v8::Unlocker unlocker;
573
574      // We shouldn't be called with any arguments, but will not complain if
575      // we are.
576      success = context->js_bindings_->MyIpAddress(&result);
577    }
578
579    if (!success)
580      return ASCIILiteralToV8String("127.0.0.1");
581    return ASCIIStringToV8String(result);
582  }
583
584  // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
585  static v8::Handle<v8::Value> MyIpAddressExCallback(
586      const v8::Arguments& args) {
587    Context* context =
588        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
589
590    std::string ip_address_list;
591    bool success;
592
593    {
594      v8::Unlocker unlocker;
595
596      // We shouldn't be called with any arguments, but will not complain if
597      // we are.
598      success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
599    }
600
601    if (!success)
602      ip_address_list = std::string();
603    return ASCIIStringToV8String(ip_address_list);
604  }
605
606  // V8 callback for when "dnsResolve()" is invoked by the PAC script.
607  static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) {
608    Context* context =
609        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
610
611    // We need at least one string argument.
612    std::string hostname;
613    if (!GetHostnameArgument(args, &hostname))
614      return v8::Null();
615
616    std::string ip_address;
617    bool success;
618
619    {
620      v8::Unlocker unlocker;
621      success = context->js_bindings_->DnsResolve(hostname, &ip_address);
622    }
623
624    return success ? ASCIIStringToV8String(ip_address) : v8::Null();
625  }
626
627  // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
628  static v8::Handle<v8::Value> DnsResolveExCallback(const v8::Arguments& args) {
629    Context* context =
630        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
631
632    // We need at least one string argument.
633    std::string hostname;
634    if (!GetHostnameArgument(args, &hostname))
635      return v8::Undefined();
636
637    std::string ip_address_list;
638    bool success;
639
640    {
641      v8::Unlocker unlocker;
642      success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
643    }
644
645    if (!success)
646      ip_address_list = std::string();
647
648    return ASCIIStringToV8String(ip_address_list);
649  }
650
651  // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
652  static v8::Handle<v8::Value> SortIpAddressListCallback(
653      const v8::Arguments& args) {
654    // We need at least one string argument.
655    if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
656      return v8::Null();
657
658    std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
659    std::string sorted_ip_address_list;
660    bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
661    if (!success)
662      return v8::False();
663    return ASCIIStringToV8String(sorted_ip_address_list);
664  }
665
666  // V8 callback for when "isInNetEx()" is invoked by the PAC script.
667  static v8::Handle<v8::Value> IsInNetExCallback(const v8::Arguments& args) {
668    // We need at least 2 string arguments.
669    if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
670        args[1].IsEmpty() || !args[1]->IsString())
671      return v8::Null();
672
673    std::string ip_address = V8StringToUTF8(args[0]->ToString());
674    std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
675    return IsInNetEx(ip_address, ip_prefix) ? v8::True() : v8::False();
676  }
677
678  ProxyResolverJSBindings* js_bindings_;
679  ProxyErrorListener* error_listener_;
680  v8::Persistent<v8::External> v8_this_;
681  v8::Persistent<v8::Context> v8_context_;
682};
683
684// ProxyResolverV8 ------------------------------------------------------------
685
686ProxyResolverV8::ProxyResolverV8(
687    ProxyResolverJSBindings* custom_js_bindings,
688    ProxyErrorListener* error_listener)
689    : context_(NULL), js_bindings_(custom_js_bindings),
690      error_listener_(error_listener) {
691
692}
693
694ProxyResolverV8::~ProxyResolverV8() {
695  if (context_ != NULL) {
696    delete context_;
697    context_ = NULL;
698  }
699  if (js_bindings_ != NULL) {
700    delete js_bindings_;
701  }
702}
703
704int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
705                                    android::String16* results) {
706  // If the V8 instance has not been initialized (either because
707  // SetPacScript() wasn't called yet, or because it failed.
708  if (context_ == NULL)
709    return ERR_FAILED;
710
711  // Otherwise call into V8.
712  int rv = context_->ResolveProxy(spec, host, results);
713
714  return rv;
715}
716
717void ProxyResolverV8::PurgeMemory() {
718  context_->PurgeMemory();
719}
720
721int ProxyResolverV8::SetPacScript(android::String16& script_data) {
722  if (context_ != NULL) {
723    delete context_;
724    context_ = NULL;
725  }
726  if (script_data.size() == 0)
727    return ERR_PAC_SCRIPT_FAILED;
728
729  // Try parsing the PAC script.
730  context_ = new Context(js_bindings_, error_listener_);
731  int rv;
732  if ((rv = context_->InitV8(script_data)) != OK) {
733    context_ = NULL;
734  }
735  if (rv != OK)
736    context_ = NULL;
737  return rv;
738}
739
740}  // namespace net
741