proxy_resolver_v8.cc revision fc93418c483ce474a1f4888b50f92574a1b81be3
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 "proxy_resolver_v8.h"
10
11#include "proxy_resolver_script.h"
12#include "net_util.h"
13#include <include/v8.h>
14#include <algorithm>
15#include <vector>
16
17#include <iostream>
18
19#include <string.h>
20
21// Notes on the javascript environment:
22//
23// For the majority of the PAC utility functions, we use the same code
24// as Firefox. See the javascript library that proxy_resolver_scipt.h
25// pulls in.
26//
27// In addition, we implement a subset of Microsoft's extensions to PAC.
28// - myIpAddressEx()
29// - dnsResolveEx()
30// - isResolvableEx()
31// - isInNetEx()
32// - sortIpAddressList()
33//
34// It is worth noting that the original PAC specification does not describe
35// the return values on failure. Consequently, there are compatibility
36// differences between browsers on what to return on failure, which are
37// illustrated below:
38//
39// --------------------+-------------+-------------------+--------------
40//                     | Firefox3    | InternetExplorer8 |  --> Us <---
41// --------------------+-------------+-------------------+--------------
42// myIpAddress()       | "127.0.0.1" |  ???              |  "127.0.0.1"
43// dnsResolve()        | null        |  false            |  null
44// myIpAddressEx()     | N/A         |  ""               |  ""
45// sortIpAddressList() | N/A         |  false            |  false
46// dnsResolveEx()      | N/A         |  ""               |  ""
47// isInNetEx()         | N/A         |  false            |  false
48// --------------------+-------------+-------------------+--------------
49//
50// TODO: The cell above reading ??? means I didn't test it.
51//
52// Another difference is in how dnsResolve() and myIpAddress() are
53// implemented -- whether they should restrict to IPv4 results, or
54// include both IPv4 and IPv6. The following table illustrates the
55// differences:
56//
57// --------------------+-------------+-------------------+--------------
58//                     | Firefox3    | InternetExplorer8 |  --> Us <---
59// --------------------+-------------+-------------------+--------------
60// myIpAddress()       | IPv4/IPv6   |  IPv4             |  IPv4
61// dnsResolve()        | IPv4/IPv6   |  IPv4             |  IPv4
62// isResolvable()      | IPv4/IPv6   |  IPv4             |  IPv4
63// myIpAddressEx()     | N/A         |  IPv4/IPv6        |  IPv4/IPv6
64// dnsResolveEx()      | N/A         |  IPv4/IPv6        |  IPv4/IPv6
65// sortIpAddressList() | N/A         |  IPv4/IPv6        |  IPv4/IPv6
66// isResolvableEx()    | N/A         |  IPv4/IPv6        |  IPv4/IPv6
67// isInNetEx()         | N/A         |  IPv4/IPv6        |  IPv4/IPv6
68// -----------------+-------------+-------------------+--------------
69
70static bool DoIsStringASCII(const std::wstring& str) {
71  for (size_t i = 0; i < str.length(); i++) {
72    unsigned char c = str[i];
73    if (c > 0x7F)
74      return false;
75  }
76  return true;
77}
78
79bool IsStringASCII(const std::wstring& str) {
80  return DoIsStringASCII(str);
81}
82
83std::string UTF16ToASCII(const std::wstring& utf16) {
84  return std::string(utf16.begin(), utf16.end());
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 std::wstring& script_data)
103      : script_data_(script_data) {}
104
105  virtual const uint16_t* data() const {
106    return reinterpret_cast<const uint16_t*>(script_data_.data());
107  }
108
109  virtual size_t length() const {
110    return script_data_.size();
111  }
112
113 private:
114  const std::wstring& 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
128  virtual const char* data() const {
129    return ascii_;
130  }
131
132  virtual size_t length() const {
133    return length_;
134  }
135
136 private:
137  const char* ascii_;
138  size_t length_;
139};
140
141// When creating a v8::String from a C++ string we have two choices: create
142// a copy, or create a wrapper that shares the same underlying storage.
143// For small strings it is better to just make a copy, whereas for large
144// strings there are savings by sharing the storage. This number identifies
145// the cutoff length for when to start wrapping rather than creating copies.
146const size_t kMaxStringBytesForCopy = 256;
147
148template <class string_type>
149inline typename string_type::value_type* WriteInto(string_type* str,
150                                                   size_t length_with_null) {
151  str->reserve(length_with_null);
152  str->resize(length_with_null - 1);
153  return &((*str)[0]);
154}
155
156// Converts a V8 String to a UTF8 std::string.
157std::string V8StringToUTF8(v8::Handle<v8::String> s) {
158  std::string result;
159  s->WriteUtf8(WriteInto(&result, s->Length() + 1));
160  return result;
161}
162
163// Converts a V8 String to a UTF16 string.
164std::wstring V8StringToUTF16(v8::Handle<v8::String> s) {
165  int len = s->Length();
166  std::wstring result;
167  // Note that the reinterpret cast is because on Windows string is an alias
168  // to wstring, and hence has character type wchar_t not uint16_t.
169  s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
170  return result;
171}
172
173// Converts an ASCII std::string to a V8 string.
174v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
175  return v8::String::New(s.data(), s.size());
176}
177
178// Converts an ASCII string literal to a V8 string.
179v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
180//  DCHECK(IsStringASCII(ascii));
181  size_t length = strlen(ascii);
182  if (length <= kMaxStringBytesForCopy)
183    return v8::String::New(ascii, length);
184  return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
185}
186
187// Stringizes a V8 object by calling its toString() method. Returns true
188// on success. This may fail if the toString() throws an exception.
189bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
190                           std::wstring* utf16_result) {
191  if (object.IsEmpty())
192    return false;
193
194  v8::HandleScope scope;
195  v8::Local<v8::String> str_object = object->ToString();
196  if (str_object.IsEmpty())
197    return false;
198  *utf16_result = V8StringToUTF16(str_object);
199  return true;
200}
201
202// Extracts an hostname argument from |args|. On success returns true
203// and fills |*hostname| with the result.
204bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) {
205  // The first argument should be a string.
206  if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
207    return false;
208
209  const std::wstring hostname_utf16 = V8StringToUTF16(args[0]->ToString());
210
211  // If the hostname is already in ASCII, simply return it as is.
212  if (IsStringASCII(hostname_utf16)) {
213    *hostname = UTF16ToASCII(hostname_utf16);
214    return true;
215  }
216  return false;
217}
218
219// Wrapper for passing around IP address strings and IPAddressNumber objects.
220struct IPAddress {
221  IPAddress(const std::string& ip_string, const IPAddressNumber& ip_number)
222      : string_value(ip_string),
223        ip_address_number(ip_number) {
224  }
225
226  // Used for sorting IP addresses in ascending order in SortIpAddressList().
227  // IP6 addresses are placed ahead of IPv4 addresses.
228  bool operator<(const IPAddress& rhs) const {
229    const IPAddressNumber& ip1 = this->ip_address_number;
230    const IPAddressNumber& ip2 = rhs.ip_address_number;
231    if (ip1.size() != ip2.size())
232      return ip1.size() > ip2.size();  // IPv6 before IPv4.
233    return memcmp(&ip1[0], &ip2[0], ip1.size()) < 0;  // Ascending order.
234  }
235
236  std::string string_value;
237  IPAddressNumber ip_address_number;
238};
239
240template<typename STR>
241bool RemoveCharsT(const STR& input,
242                  const typename STR::value_type remove_chars[],
243                  STR* output) {
244  bool removed = false;
245  size_t found;
246
247  *output = input;
248
249  found = output->find_first_of(remove_chars);
250  while (found != STR::npos) {
251    removed = true;
252    output->replace(found, 1, STR());
253    found = output->find_first_of(remove_chars, found);
254  }
255
256  return removed;
257}
258
259bool RemoveChars(const std::wstring& input,
260                 const wchar_t remove_chars[],
261                 std::wstring* output) {
262  return RemoveCharsT(input, remove_chars, output);
263}
264
265bool 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.
278bool 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(tok_list, ";");
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).
324bool IsInNetEx(const std::string& ip_address, const std::string& ip_prefix) {
325  IPAddressNumber address;
326  if (!ParseIPLiteralToNumber(ip_address, &address))
327    return false;
328
329  IPAddressNumber prefix;
330  size_t prefix_length_in_bits;
331  if (!ParseCIDRBlock(ip_prefix, &prefix, &prefix_length_in_bits))
332    return false;
333
334  // Both |address| and |prefix| must be of the same type (IPv4 or IPv6).
335  if (address.size() != prefix.size())
336    return false;
337
338  return IPNumberMatchesPrefix(address, prefix, prefix_length_in_bits);
339}
340
341}  // namespace
342
343// ProxyResolverV8::Context ---------------------------------------------------
344
345class ProxyResolverV8::Context {
346 public:
347  explicit Context(ProxyResolverJSBindings* js_bindings)
348      : js_bindings_(js_bindings) {
349  }
350
351  ~Context() {
352    v8::Locker locked;
353
354    v8_this_.Dispose();
355    v8_context_.Dispose();
356
357    // Run the V8 garbage collector. We do this to be sure the
358    // ExternalStringResource objects we allocated get properly disposed.
359    // Otherwise when running the unit-tests they may get leaked.
360    // See crbug.com/48145.
361    PurgeMemory();
362  }
363
364  int ResolveProxy(const std::string url, const std::string host, std::string* results) {
365    v8::Locker locked;
366    v8::HandleScope scope;
367
368    v8::Context::Scope function_scope(v8_context_);
369
370    v8::Local<v8::Value> function;
371    if (!GetFindProxyForURL(&function)) {
372      *results = "FindProxyForURL() is undefined";
373      return ERR_PAC_SCRIPT_FAILED;
374    }
375
376    v8::Handle<v8::Value> argv[] = {
377        ASCIIStringToV8String(url),
378        ASCIIStringToV8String(host) };
379
380    v8::TryCatch try_catch;
381    v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
382        v8_context_->Global(), 2, argv);
383
384    if (try_catch.HasCaught()) {
385      *results = V8StringToUTF8(try_catch.Message()->Get());
386      return ERR_PAC_SCRIPT_FAILED;
387    }
388
389    if (!ret->IsString()) {
390      *results = "FindProxyForURL() did not return a string.";
391      return ERR_PAC_SCRIPT_FAILED;
392    }
393
394    std::wstring ret_str = V8StringToUTF16(ret->ToString());
395
396    if (!IsStringASCII(ret_str)) {
397      // TODO:         Rather than failing when a wide string is returned, we
398      //               could extend the parsing to handle IDNA hostnames by
399      //               converting them to ASCII punycode.
400      //               crbug.com/47234
401      *results = "FindProxyForURL() returned a non-ASCII string";
402      return ERR_PAC_SCRIPT_FAILED;
403    }
404
405    *results = V8StringToUTF8(ret->ToString());
406    return OK;
407  }
408
409  int InitV8(const std::string& pac_script) {
410    v8::Locker locked;
411    v8::HandleScope scope;
412
413    v8_this_ = v8::Persistent<v8::External>::New(v8::External::New(this));
414    v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
415
416    // Attach the javascript bindings.
417    v8::Local<v8::FunctionTemplate> alert_template =
418        v8::FunctionTemplate::New(&AlertCallback, v8_this_);
419    global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
420
421    v8::Local<v8::FunctionTemplate> my_ip_address_template =
422        v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
423    global_template->Set(ASCIILiteralToV8String("myIpAddress"),
424        my_ip_address_template);
425
426    v8::Local<v8::FunctionTemplate> dns_resolve_template =
427        v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
428    global_template->Set(ASCIILiteralToV8String("dnsResolve"),
429        dns_resolve_template);
430
431    // Microsoft's PAC extensions:
432
433    v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
434        v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
435    global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
436                         dns_resolve_ex_template);
437
438    v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
439        v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
440    global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
441                         my_ip_address_ex_template);
442
443    v8::Local<v8::FunctionTemplate> sort_ip_address_list_template =
444        v8::FunctionTemplate::New(&SortIpAddressListCallback, v8_this_);
445    global_template->Set(ASCIILiteralToV8String("sortIpAddressList"),
446                         sort_ip_address_list_template);
447
448    v8::Local<v8::FunctionTemplate> is_in_net_ex_template =
449        v8::FunctionTemplate::New(&IsInNetExCallback, v8_this_);
450    global_template->Set(ASCIILiteralToV8String("isInNetEx"),
451                         is_in_net_ex_template);
452
453    v8_context_ = v8::Context::New(NULL, global_template);
454
455    v8::Context::Scope ctx(v8_context_);
456
457    // Add the PAC utility functions to the environment.
458    // (This script should never fail, as it is a string literal!)
459    // Note that the two string literals are concatenated.
460    int rv = RunScript(
461        ASCIILiteralToV8String(
462            PROXY_RESOLVER_SCRIPT
463            PROXY_RESOLVER_SCRIPT_EX),
464        kPacUtilityResourceName);
465    if (rv != OK) {
466      return rv;
467    }
468
469    // Add the user's PAC code to the environment.
470    rv = RunScript(ASCIIStringToV8String(pac_script), kPacResourceName);
471    if (rv != OK) {
472      return rv;
473    }
474
475    // At a minimum, the FindProxyForURL() function must be defined for this
476    // to be a legitimiate PAC script.
477    v8::Local<v8::Value> function;
478    if (!GetFindProxyForURL(&function))
479      return ERR_PAC_SCRIPT_FAILED;
480
481    return OK;
482  }
483
484  void PurgeMemory() {
485    v8::Locker locked;
486    // Repeatedly call the V8 idle notification until it returns true ("nothing
487    // more to free").  Note that it makes more sense to do this than to
488    // implement a new "delete everything" pass because object references make
489    // it difficult to free everything possible in just one pass.
490    while (!v8::V8::IdleNotification())
491      ;
492  }
493
494 private:
495  bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
496    *function = v8_context_->Global()->Get(
497        ASCIILiteralToV8String("FindProxyForURL"));
498    return (*function)->IsFunction();
499  }
500
501  // Handle an exception thrown by V8.
502  void HandleError(v8::Handle<v8::Message> message) {
503    if (message.IsEmpty())
504      return;
505  }
506
507  // Compiles and runs |script| in the current V8 context.
508  // Returns OK on success, otherwise an error code.
509  int RunScript(v8::Handle<v8::String> script, const char* script_name) {
510    v8::TryCatch try_catch;
511
512    // Compile the script.
513    v8::ScriptOrigin origin =
514        v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
515    v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
516
517    // Execute.
518    if (!code.IsEmpty())
519      code->Run();
520
521    // Check for errors.
522    if (try_catch.HasCaught()) {
523      HandleError(try_catch.Message());
524      return ERR_PAC_SCRIPT_FAILED;
525    }
526
527    return OK;
528  }
529
530  // V8 callback for when "alert()" is invoked by the PAC script.
531  static v8::Handle<v8::Value> AlertCallback(const v8::Arguments& args) {
532    Context* context =
533        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
534
535    // Like firefox we assume "undefined" if no argument was specified, and
536    // disregard any arguments beyond the first.
537    std::wstring message;
538    if (args.Length() == 0) {
539      std::string undef = "undefined";
540      std::wstring wundef(undef.begin(), undef.end());
541      message = wundef;
542    } else {
543      if (!V8ObjectToUTF16String(args[0], &message))
544        return v8::Undefined();  // toString() threw an exception.
545    }
546
547    context->js_bindings_->Alert(message);
548    return v8::Undefined();
549  }
550
551  // V8 callback for when "myIpAddress()" is invoked by the PAC script.
552  static v8::Handle<v8::Value> MyIpAddressCallback(const v8::Arguments& args) {
553    Context* context =
554        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
555
556    std::string result;
557    bool success;
558
559    {
560      v8::Unlocker unlocker;
561
562      // We shouldn't be called with any arguments, but will not complain if
563      // we are.
564      success = context->js_bindings_->MyIpAddress(&result);
565    }
566
567    if (!success)
568      return ASCIILiteralToV8String("127.0.0.1");
569    return ASCIIStringToV8String(result);
570  }
571
572  // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
573  static v8::Handle<v8::Value> MyIpAddressExCallback(
574      const v8::Arguments& args) {
575    Context* context =
576        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
577
578    std::string ip_address_list;
579    bool success;
580
581    {
582      v8::Unlocker unlocker;
583
584      // We shouldn't be called with any arguments, but will not complain if
585      // we are.
586      success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
587    }
588
589    if (!success)
590      ip_address_list = std::string();
591    return ASCIIStringToV8String(ip_address_list);
592  }
593
594  // V8 callback for when "dnsResolve()" is invoked by the PAC script.
595  static v8::Handle<v8::Value> DnsResolveCallback(const v8::Arguments& args) {
596    Context* context =
597        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
598
599    // We need at least one string argument.
600    std::string hostname;
601    if (!GetHostnameArgument(args, &hostname))
602      return v8::Null();
603
604    std::string ip_address;
605    bool success;
606
607    {
608      v8::Unlocker unlocker;
609      success = context->js_bindings_->DnsResolve(hostname, &ip_address);
610    }
611
612    return success ? ASCIIStringToV8String(ip_address) : v8::Null();
613  }
614
615  // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
616  static v8::Handle<v8::Value> DnsResolveExCallback(const v8::Arguments& args) {
617    Context* context =
618        static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
619
620    // We need at least one string argument.
621    std::string hostname;
622    if (!GetHostnameArgument(args, &hostname))
623      return v8::Undefined();
624
625    std::string ip_address_list;
626    bool success;
627
628    {
629      v8::Unlocker unlocker;
630      success = context->js_bindings_->DnsResolveEx(hostname, &ip_address_list);
631    }
632
633    if (!success)
634      ip_address_list = std::string();
635
636    return ASCIIStringToV8String(ip_address_list);
637  }
638
639  // V8 callback for when "sortIpAddressList()" is invoked by the PAC script.
640  static v8::Handle<v8::Value> SortIpAddressListCallback(
641      const v8::Arguments& args) {
642    // We need at least one string argument.
643    if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
644      return v8::Null();
645
646    std::string ip_address_list = V8StringToUTF8(args[0]->ToString());
647    std::string sorted_ip_address_list;
648    bool success = SortIpAddressList(ip_address_list, &sorted_ip_address_list);
649    if (!success)
650      return v8::False();
651    return ASCIIStringToV8String(sorted_ip_address_list);
652  }
653
654  // V8 callback for when "isInNetEx()" is invoked by the PAC script.
655  static v8::Handle<v8::Value> IsInNetExCallback(const v8::Arguments& args) {
656    // We need at least 2 string arguments.
657    if (args.Length() < 2 || args[0].IsEmpty() || !args[0]->IsString() ||
658        args[1].IsEmpty() || !args[1]->IsString())
659      return v8::Null();
660
661    std::string ip_address = V8StringToUTF8(args[0]->ToString());
662    std::string ip_prefix = V8StringToUTF8(args[1]->ToString());
663    return IsInNetEx(ip_address, ip_prefix) ? v8::True() : v8::False();
664  }
665
666  ProxyResolverJSBindings* js_bindings_;
667  v8::Persistent<v8::External> v8_this_;
668  v8::Persistent<v8::Context> v8_context_;
669};
670
671// ProxyResolverV8 ------------------------------------------------------------
672
673ProxyResolverV8::ProxyResolverV8(
674    ProxyResolverJSBindings* custom_js_bindings)
675    : context_(NULL), js_bindings_(custom_js_bindings) {
676}
677
678ProxyResolverV8::~ProxyResolverV8() {
679
680}
681
682int ProxyResolverV8::GetProxyForURL(const std::string spec, const std::string host,
683                                    std::string* results) {
684  // If the V8 instance has not been initialized (either because
685  // SetPacScript() wasn't called yet, or because it failed.
686  if (context_ == NULL)
687    return ERR_FAILED;
688
689  // Otherwise call into V8.
690  int rv = context_->ResolveProxy(spec, host, results);
691
692  return rv;
693}
694
695void ProxyResolverV8::CancelRequest(RequestHandle request) {
696}
697
698void ProxyResolverV8::CancelSetPacScript() {
699}
700
701void ProxyResolverV8::PurgeMemory() {
702  context_->PurgeMemory();
703}
704
705void ProxyResolverV8::Shutdown() {
706}
707
708int ProxyResolverV8::SetPacScript(std::string& script_data) {
709  if (context_ != NULL) {
710    delete context_;
711  }
712  if (script_data.empty())
713    return ERR_PAC_SCRIPT_FAILED;
714
715  // Try parsing the PAC script.
716  context_ = new Context(js_bindings_);
717  int rv;
718  if ((rv = context_->InitV8(script_data)) != OK) {
719    context_ = NULL;
720  }
721  if (rv != OK)
722    context_ = NULL;
723  return rv;
724}
725
726}  // namespace net
727