Home | History | Annotate | Download | only in src
      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 
     74 static 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 
     83 bool IsStringASCII(const android::String16& str) {
     84   return DoIsStringASCII(str);
     85 }
     86 
     87 namespace net {
     88 
     89 namespace {
     90 
     91 // Pseudo-name for the PAC script.
     92 const char kPacResourceName[] = "proxy-pac-script.js";
     93 // Pseudo-name for the PAC utility script.
     94 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
     95 
     96 // External string wrapper so V8 can access the UTF16 string wrapped by
     97 // ProxyResolverScriptData.
     98 class 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.
    119 class 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.
    145 const size_t kMaxStringBytesForCopy = 256;
    146 
    147 template <class string_type>
    148 inline 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.
    156 std::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.
    163 android::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 
    172 std::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.
    178 v8::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 
    182 v8::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.
    187 v8::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.
    197 bool 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.
    213 bool 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.
    229 struct 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 
    249 template<typename STR>
    250 bool 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 
    268 bool 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.
    281 bool 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).
    327 bool 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 
    351 class 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     isolate_->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 
    707 ProxyResolverV8::ProxyResolverV8(
    708     ProxyResolverJSBindings* custom_js_bindings,
    709     ProxyErrorListener* error_listener)
    710     : context_(NULL), js_bindings_(custom_js_bindings),
    711       error_listener_(error_listener) {
    712   v8::V8::Initialize();
    713 }
    714 
    715 ProxyResolverV8::~ProxyResolverV8() {
    716   if (context_ != NULL) {
    717     delete context_;
    718     context_ = NULL;
    719   }
    720   if (js_bindings_ != NULL) {
    721     delete js_bindings_;
    722   }
    723 }
    724 
    725 int 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 
    738 void ProxyResolverV8::PurgeMemory() {
    739   context_->PurgeMemory();
    740 }
    741 
    742 int 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