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