Merge "Add default code reviewers into OWNERS"
diff --git a/Android.bp b/Android.bp
index 8c38aaa..bf5c328 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
 
     srcs: [
         "src/proxy_resolver_v8.cc",
+        "src/proxy_resolver_v8_wrapper.cc",
         "src/proxy_resolver_js_bindings.cc",
         "src/net_util.cc",
     ],
@@ -15,14 +16,24 @@
         "-Werror",
     ],
 
-    export_include_dirs: ["src"],
+    export_include_dirs: ["includes"],
+    local_include_dirs: ["src"],
 
-    static_libs: ["libv8"],
+    static_libs: [
+        "libv8",
+        // libpac lives in Runtime APEX. It can't use the shared liblog in /system/.
+        "liblog",
+    ],
 
     shared_libs: [
-        "libutils",
-        "liblog",
         "libicuuc",
         "libicui18n",
     ],
+
+    stubs: {
+        symbol_file: "libpac.map.txt",
+        versions: [
+            "1",
+        ],
+    },
 }
diff --git a/includes/proxy_resolver_v8_wrapper.h b/includes/proxy_resolver_v8_wrapper.h
new file mode 100644
index 0000000..776a45d
--- /dev/null
+++ b/includes/proxy_resolver_v8_wrapper.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NET_PROXY_PROXY_RESOLVER_V8_WRAPPER_H_
+#define NET_PROXY_PROXY_RESOLVER_V8_WRAPPER_H_
+
+// This header should be compatible with C or C++ compiler
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OK 0
+#define ERR_PAC_SCRIPT_FAILED -1
+#define ERR_FAILED -2
+
+/**
+* Declare an incomplete type ProxyResolverV8Handle. A pointer of this should always be
+* either null or pointing to a C++ ProxyResolverV8 object.
+*/
+typedef void* ProxyResolverV8Handle;
+
+/**
+ * Create a ProxyResolverV8Handle. The caller must call ProxyResolverV8Handle_delete to release
+ * the memory.
+ */
+ProxyResolverV8Handle* ProxyResolverV8Handle_new();
+
+/**
+ * @return the result in char16_t array if the run is successful. The result contains a list of
+ *         proxies according to the PAC specification. Otherwise, return NULL if the run fails.
+ *         The memory is allocated with malloc() and the caller should use free() to release it
+ *         after use.
+ */
+char16_t* ProxyResolverV8Handle_GetProxyForURL(ProxyResolverV8Handle* handle,
+        const char16_t* spec, const char16_t* host);
+/**
+ * @return OK if setting the pac script successfully.
+ */
+int ProxyResolverV8Handle_SetPacScript(ProxyResolverV8Handle* handle,
+        const char16_t* script_data);
+
+void ProxyResolverV8Handle_delete(ProxyResolverV8Handle* handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NET_PROXY_PROXY_RESOLVER_V8_WRAPPER_H_
\ No newline at end of file
diff --git a/libpac.map.txt b/libpac.map.txt
new file mode 100644
index 0000000..1936fa9
--- /dev/null
+++ b/libpac.map.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LIBPAC_EXTERNAL_1 {
+  global:
+    ProxyResolverV8Handle_new;
+    ProxyResolverV8Handle_GetProxyForURL;
+    ProxyResolverV8Handle_SetPacScript;
+    ProxyResolverV8Handle_delete;
+  local:
+    *;
+};
\ No newline at end of file
diff --git a/src/proxy_resolver_js_bindings.h b/src/proxy_resolver_js_bindings.h
index 9559492..ebdacfe 100644
--- a/src/proxy_resolver_js_bindings.h
+++ b/src/proxy_resolver_js_bindings.h
@@ -6,13 +6,10 @@
 #define NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
 #pragma once
 
-#include <utils/String16.h>
 #include <string>
 
 namespace net {
 
-class ProxyErrorListener;
-
 // Interface for the javascript bindings.
 class ProxyResolverJSBindings {
  public:
diff --git a/src/proxy_resolver_v8.cc b/src/proxy_resolver_v8.cc
index e37334c..f26ff9d 100644
--- a/src/proxy_resolver_v8.cc
+++ b/src/proxy_resolver_v8.cc
@@ -2,13 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef ANDROID
+#define LOG_TAG "libpac"
+#include <utils/Log.h>
+#endif
+
 #include "proxy_resolver_v8.h"
 
 #include <algorithm>
 #include <cstdio>
 #include <iostream>
 #include <string>
-#include <utils/String8.h>
 #include <v8.h>
 #include <libplatform/libplatform.h>
 #include <vector>
@@ -65,50 +69,49 @@
 // isInNetEx()         | N/A         |  IPv4/IPv6        |  IPv4/IPv6
 // -----------------+-------------+-------------------+--------------
 
-static bool DoIsStringASCII(const android::String16& str) {
-  for (size_t i = 0; i < str.size(); i++) {
-    unsigned short c = str.string()[i];
+static bool DoIsStringASCII(const std::u16string& str) {
+  for (size_t i = 0; i < str.length(); i++) {
+    unsigned short c = str.data()[i];
     if (c > 0x7F)
       return false;
   }
   return true;
 }
 
-bool IsStringASCII(const android::String16& str) {
+bool IsStringASCII(const std::u16string& str) {
   return DoIsStringASCII(str);
 }
 
+#ifdef ANDROID
+#define LOG_ALERT(...) ALOGD(__VA_ARGS__)
+#define LOG_ERROR(...) ALOGE(__VA_ARGS__)
+#else
+#define LOG_ALERT(...) vprintf(__VA_ARGS__)
+#define LOG_ERROR(...) vfprintf(stderr, __VA_ARGS__)
+#endif
+
 namespace net {
 
 namespace {
 
+class DefaultProxyErrorListener : public ProxyErrorListener {
+public:
+    ~DefaultProxyErrorListener() {}
+    void AlertMessage(const std::string& message) {
+        LOG_ALERT("Alert: %s", message.data());
+    }
+    void ErrorMessage(const std::string& message) {
+        LOG_ERROR("Error: %s", message.data());
+    }
+};
+
+ProxyErrorListener* const defaultProxyErrorListener = new DefaultProxyErrorListener();
+
 // Pseudo-name for the PAC script.
 const char kPacResourceName[] = "proxy-pac-script.js";
 // Pseudo-name for the PAC utility script.
 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
 
-// External string wrapper so V8 can access the UTF16 string wrapped by
-// ProxyResolverScriptData.
-class V8ExternalStringFromScriptData
-    : public v8::String::ExternalStringResource {
- public:
-  explicit V8ExternalStringFromScriptData(
-      const android::String16& script_data)
-      : script_data_(script_data) {}
-
-  virtual const uint16_t* data() const {
-    return reinterpret_cast<const uint16_t*>(script_data_.string());
-  }
-
-  virtual size_t length() const {
-    return script_data_.size();
-  }
-
- private:
-  const android::String16& script_data_;
-//  DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
-};
-
 // External string wrapper so V8 can access a string literal.
 class V8ExternalASCIILiteral
     : public v8::String::ExternalOneByteStringResource {
@@ -155,28 +158,23 @@
 }
 
 // Converts a V8 String to a UTF16 string.
-android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
+std::u16string V8StringToUTF16(v8::Handle<v8::String> s) {
   int len = s->Length();
   char16_t* buf = new char16_t[len + 1];
   s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
-  android::String16 ret(buf, len);
+  std::u16string ret(buf, len);
   delete[] buf;
   return ret;
 }
 
-std::string UTF16ToASCII(const android::String16& str) {
-  android::String8 rstr(str);
-  return std::string(rstr.string());
-}
-
 // Converts an ASCII std::string to a V8 string.
 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
   return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
 }
 
-v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
+v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const std::u16string& s) {
   return v8::String::NewFromTwoByte(
-      isolate, reinterpret_cast<const uint16_t*>(s.string()),
+      isolate, reinterpret_cast<const uint16_t*>(s.data()),
       v8::String::kNormalString, s.size());
 }
 
@@ -191,8 +189,8 @@
 
 // Stringizes a V8 object by calling its toString() method. Returns true
 // on success. This may fail if the toString() throws an exception.
-bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
-                           android::String16* utf16_result,
+bool V8ObjectToUTF8String(v8::Handle<v8::Value> object,
+                           std::string* utf8_result,
                            v8::Isolate* isolate) {
   if (object.IsEmpty())
     return false;
@@ -201,7 +199,7 @@
   v8::Local<v8::String> str_object = object->ToString();
   if (str_object.IsEmpty())
     return false;
-  *utf16_result = V8StringToUTF16(str_object);
+  *utf8_result = V8StringToUTF8(str_object);
   return true;
 }
 
@@ -212,11 +210,11 @@
   if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
     return false;
 
-  const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
+  const std::u16string hostname_utf16 = V8StringToUTF16(args[0]->ToString());
 
   // If the hostname is already in ASCII, simply return it as is.
   if (IsStringASCII(hostname_utf16)) {
-    *hostname = UTF16ToASCII(hostname_utf16);
+    *hostname = V8StringToUTF8(args[0]->ToString());
     return true;
   }
   return false;
@@ -371,8 +369,8 @@
     v8_context_.Reset();
   }
 
-  int ResolveProxy(const android::String16 url, const android::String16 host,
-        android::String16* results) {
+  int ResolveProxy(const std::u16string url, const std::u16string host,
+        std::u16string* results) {
     v8::Locker locked(isolate_);
     v8::Isolate::Scope isolate_scope(isolate_);
     v8::HandleScope scope(isolate_);
@@ -383,8 +381,7 @@
 
     v8::Local<v8::Value> function;
     if (!GetFindProxyForURL(&function)) {
-      error_listener_->ErrorMessage(
-          android::String16("FindProxyForURL() is undefined"));
+      error_listener_->ErrorMessage("FindProxyForURL() is undefined");
       return ERR_PAC_SCRIPT_FAILED;
     }
 
@@ -397,14 +394,12 @@
         context->Global(), 2, argv);
 
     if (try_catch.HasCaught()) {
-      error_listener_->ErrorMessage(
-          V8StringToUTF16(try_catch.Message()->Get()));
+      error_listener_->ErrorMessage(V8StringToUTF8(try_catch.Message()->Get()));
       return ERR_PAC_SCRIPT_FAILED;
     }
 
     if (!ret->IsString()) {
-      error_listener_->ErrorMessage(
-          android::String16("FindProxyForURL() did not return a string."));
+      error_listener_->ErrorMessage("FindProxyForURL() did not return a string.");
       return ERR_PAC_SCRIPT_FAILED;
     }
 
@@ -415,15 +410,14 @@
       //               could extend the parsing to handle IDNA hostnames by
       //               converting them to ASCII punycode.
       //               crbug.com/47234
-      error_listener_->ErrorMessage(
-          android::String16("FindProxyForURL() returned a non-ASCII string"));
+      error_listener_->ErrorMessage("FindProxyForURL() returned a non-ASCII string");
       return ERR_PAC_SCRIPT_FAILED;
     }
 
     return OK;
   }
 
-  int InitV8(const android::String16& pac_script) {
+  int InitV8(const std::u16string& pac_script) {
     v8::Locker locked(isolate_);
     v8::Isolate::Scope isolate_scope(isolate_);
     v8::HandleScope scope(isolate_);
@@ -523,7 +517,7 @@
   void HandleError(v8::Handle<v8::Message> message) {
     if (message.IsEmpty())
       return;
-    error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
+    error_listener_->ErrorMessage(V8StringToUTF8(message->Get()));
   }
 
   // Compiles and runs |script| in the current V8 context.
@@ -556,14 +550,11 @@
 
     // Like firefox we assume "undefined" if no argument was specified, and
     // disregard any arguments beyond the first.
-    android::String16 message;
+    std::string message;
     if (args.Length() == 0) {
-      std::string undef = "undefined";
-      android::String8 undef8(undef.c_str());
-      android::String16 wundef(undef8);
-      message = wundef;
+      message = "undefined";
     } else {
-      if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
+      if (!V8ObjectToUTF8String(args[0], &message, args.GetIsolate()))
         return;  // toString() threw an exception.
     }
 
@@ -714,6 +705,10 @@
 
 bool ProxyResolverV8::initialized_for_this_process_ = false;
 
+ProxyResolverV8::ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings)
+    : ProxyResolverV8(custom_js_bindings, defaultProxyErrorListener) {
+}
+
 ProxyResolverV8::ProxyResolverV8(
     ProxyResolverJSBindings* custom_js_bindings,
     ProxyErrorListener* error_listener)
@@ -737,13 +732,14 @@
   }
 }
 
-int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
-                                    android::String16* results) {
+int ProxyResolverV8::GetProxyForURL(const std::u16string& spec, const std::u16string& host,
+                                    std::u16string* results) {
   // If the V8 instance has not been initialized (either because
   // SetPacScript() wasn't called yet, or because it failed.
-  if (context_ == NULL)
+  if (context_ == NULL) {
+    error_listener_->ErrorMessage(std::string("Context is null."));
     return ERR_FAILED;
-
+  }
   // Otherwise call into V8.
   int rv = context_->ResolveProxy(spec, host, results);
 
@@ -754,12 +750,12 @@
   context_->PurgeMemory();
 }
 
-int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
+int ProxyResolverV8::SetPacScript(const std::u16string& script_data) {
   if (context_ != NULL) {
     delete context_;
     context_ = NULL;
   }
-  if (script_data.size() == 0)
+  if (script_data.length() == 0)
     return ERR_PAC_SCRIPT_FAILED;
 
   // Use the built-in locale-aware definitions instead of the ones provided by
diff --git a/src/proxy_resolver_v8.h b/src/proxy_resolver_v8.h
index 9b74e29..36a3214 100644
--- a/src/proxy_resolver_v8.h
+++ b/src/proxy_resolver_v8.h
@@ -6,25 +6,19 @@
 #define NET_PROXY_PROXY_RESOLVER_V8_H_
 #pragma once
 
-#include <utils/String16.h>
+#include <string>
 
 #include "proxy_resolver_js_bindings.h"
+#include "proxy_resolver_v8_wrapper.h"
 
 namespace net {
 
-typedef void* RequestHandle;
-typedef void* CompletionCallback;
-
-#define OK 0
-#define ERR_PAC_SCRIPT_FAILED -1
-#define ERR_FAILED -2
-
 class ProxyErrorListener {
 protected:
   virtual ~ProxyErrorListener() {}
 public:
-  virtual void AlertMessage(android::String16 message) = 0;
-  virtual void ErrorMessage(android::String16 error) = 0;
+  virtual void AlertMessage(const std::string& message) = 0;
+  virtual void ErrorMessage(const std::string& error) = 0;
 };
 
 // Implementation of ProxyResolver that uses V8 to evaluate PAC scripts.
@@ -50,6 +44,8 @@
   // Constructs a ProxyResolverV8 with custom bindings. ProxyResolverV8 takes
   // ownership of |custom_js_bindings| and deletes it when ProxyResolverV8
   // is destroyed.
+  explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings);
+  // This constructor should only be used for test.
   explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings,
           ProxyErrorListener* error_listener);
 
@@ -57,10 +53,10 @@
 
   ProxyResolverJSBindings* js_bindings() { return js_bindings_; }
 
-  virtual int GetProxyForURL(const android::String16 spec, const android::String16 host,
-                             android::String16* results);
+  virtual int GetProxyForURL(const std::u16string& spec, const std::u16string& host,
+                             std::u16string* results);
   virtual void PurgeMemory();
-  virtual int SetPacScript(const android::String16& script_data);
+  virtual int SetPacScript(const std::u16string& script_data);
 
  private:
   // Context holds the Javascript state for the most recently loaded PAC
diff --git a/src/proxy_resolver_v8_wrapper.cc b/src/proxy_resolver_v8_wrapper.cc
new file mode 100644
index 0000000..ae56d9a
--- /dev/null
+++ b/src/proxy_resolver_v8_wrapper.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "proxy_resolver_v8_wrapper.h"
+#include "proxy_resolver_v8.h"
+
+using namespace net;
+
+// Implement C interface of ProxyResolverV8
+extern "C" {
+
+ProxyResolverV8Handle* ProxyResolverV8Handle_new() {
+  ProxyResolverV8* proxyResolver = new ProxyResolverV8(ProxyResolverJSBindings::CreateDefault());
+  return reinterpret_cast<ProxyResolverV8Handle*>(proxyResolver);
+}
+
+char16_t* ProxyResolverV8Handle_GetProxyForURL(ProxyResolverV8Handle* handle,
+                                               const char16_t* spec,
+                                               const char16_t* host) {
+  ProxyResolverV8* proxyResolver = reinterpret_cast<ProxyResolverV8*>(handle);
+  std::u16string specStr(spec);
+  std::u16string hostStr(host);
+  std::u16string proxies;
+  int code = proxyResolver->GetProxyForURL(specStr, hostStr, &proxies);
+  if (code != OK) {
+    return NULL;
+  }
+  auto len = proxies.length();
+  char16_t* result = (char16_t*) malloc(sizeof(char16_t) * (len + 1));
+  if (result == 0) { // Failed to allocate
+    return NULL;
+  }
+  proxies.copy(result, len);
+  result[len] = u'\0';
+  return result;
+}
+
+int ProxyResolverV8Handle_SetPacScript(ProxyResolverV8Handle* handle,
+        const char16_t* script_data) {
+  ProxyResolverV8* proxyResolver = reinterpret_cast<ProxyResolverV8*>(handle);
+  std::u16string script(script_data);
+  return proxyResolver->SetPacScript(script);
+}
+
+void ProxyResolverV8Handle_delete(ProxyResolverV8Handle* handle) {
+  ProxyResolverV8* proxyResolver = reinterpret_cast<ProxyResolverV8*>(handle);
+  delete proxyResolver;
+}
+
+} // extern "C"
\ No newline at end of file
diff --git a/test/Android.bp b/test/Android.bp
index 24f2ddc..656dfaa 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -3,6 +3,8 @@
 
     srcs: ["proxy_resolver_v8_unittest.cc"],
 
+    include_dirs: ["external/chromium-libpac/src"],
+
     cflags: [
         "-Wno-endif-labels",
         "-Wno-import",
diff --git a/test/jstocstring.pl b/test/jstocstring.pl
index 7b50772..eade2b8 100755
--- a/test/jstocstring.pl
+++ b/test/jstocstring.pl
@@ -15,6 +15,7 @@
     if (s/\.js/_JS/) {
         $upper = uc();
         print FILE "#define $upper \\\n";
+        print FILE "  u\"\"\\\n";
         while (<FH>) {
             s/\"/\\\"/g;
             chomp();
diff --git a/test/proxy_resolver_v8_unittest.cc b/test/proxy_resolver_v8_unittest.cc
index a13f5e5..b549cda 100644
--- a/test/proxy_resolver_v8_unittest.cc
+++ b/test/proxy_resolver_v8_unittest.cc
@@ -11,9 +11,9 @@
 
 #include "android_runtime/AndroidRuntime.h"
 #include "proxy_test_script.h"
+#include "proxy_resolver_v8_wrapper.h"
 #include "proxy_resolver_v8.h"
 
-using namespace android;
 namespace net {
 namespace {
 
@@ -49,20 +49,14 @@
     return !dns_resolve_ex_result.empty();
   }
 
-  virtual void AlertMessage(String16 message) {
-    String8 m8(message);
-    std::string mstd(m8.string());
-
-    ALOGD("PAC-alert: %s\n", mstd.c_str());  // Helpful when debugging.
-    alerts.push_back(mstd);
+  virtual void AlertMessage(const std::string& message) {
+    ALOGD("PAC-alert: %s\n", message.c_str());  // Helpful when debugging.
+    alerts.push_back(message);
   }
 
-  virtual void ErrorMessage(const String16 message) {
-    String8 m8(message);
-    std::string mstd(m8.string());
-
-    ALOGD("PAC-error: %s\n", mstd.c_str());  // Helpful when debugging.
-    errors.push_back(mstd);
+  virtual void ErrorMessage(const std::string& message) {
+    ALOGD("PAC-error: %s\n", message.c_str());  // Helpful when debugging.
+    errors.push_back(message);
   }
 
   virtual void Shutdown() {}
@@ -100,12 +94,12 @@
 };
 
 // Doesn't really matter what these values are for many of the tests.
-const String16 kQueryUrl("http://www.google.com");
-const String16 kQueryHost("www.google.com");
-String16 kResults;
+const std::u16string kQueryUrl(u"http://www.google.com");
+const std::u16string kQueryHost(u"www.google.com");
+std::u16string kResults;
 
-String16 currentPac;
-#define SCRIPT(x) (currentPac = String16(x))
+std::u16string currentPac;
+#define SCRIPT(x) (currentPac = std::u16string(x))
 
 void addString(std::vector<std::string>* list, std::string str) {
   if (str.compare(0, 6, "DIRECT") == 0) {
@@ -117,9 +111,21 @@
   }
 }
 
-std::vector<std::string> string16ToProxyList(String16 response) {
+std::unique_ptr<ProxyResolverV8Handle, void(*)(ProxyResolverV8Handle*)> newProxyResolverV8Handle() {
+  return std::unique_ptr<ProxyResolverV8Handle, void(*)(ProxyResolverV8Handle*)>(
+          ProxyResolverV8Handle_new(), &ProxyResolverV8Handle_delete);
+}
+
+std::unique_ptr<char16_t, decltype(&free)> getProxyForURL(ProxyResolverV8Handle* handle,
+                                                         const char16_t* spec,
+                                                         const char16_t* host) {
+  return std::unique_ptr<char16_t, decltype(&free)>(
+      ProxyResolverV8Handle_GetProxyForURL(handle, spec, host), &free);
+}
+
+std::vector<std::string> string16ToProxyList(const std::u16string& response) {
     std::vector<std::string> ret;
-    String8 response8(response);
+    android::String8 response8(response.data());
     std::string rstr(response8.string());
     if (rstr.find(';') == std::string::npos) {
         addString(&ret, rstr);
@@ -163,6 +169,21 @@
   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
 }
 
+TEST(ProxyResolverV8Test, Direct_C_API) {
+  auto handle = newProxyResolverV8Handle();
+  int result = ProxyResolverV8Handle_SetPacScript(handle.get(), DIRECT_JS);
+  EXPECT_EQ(OK, result);
+
+  std::unique_ptr<char16_t, decltype(&free)> result_list = getProxyForURL(
+      handle.get(), kQueryUrl.data(), kQueryHost.data());
+
+  ASSERT_NE(nullptr, result_list.get());
+  kResults = result_list.get();
+  std::vector<std::string> proxies = string16ToProxyList(kResults);
+  EXPECT_EQ(proxies.size(), 1U);
+  EXPECT_EQ("DIRECT",proxies[0]);
+}
+
 TEST(ProxyResolverV8Test, ReturnEmptyString) {
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
   int result = resolver.SetPacScript(SCRIPT(RETURN_EMPTY_STRING_JS));
@@ -187,8 +208,8 @@
   // arguments into a pseudo-host. The purpose of this test is to verify that
   // the correct arguments are being passed to FindProxyForURL().
   {
-    String16 queryUrl("http://query.com/path");
-    String16 queryHost("query.com");
+    std::u16string queryUrl(u"http://query.com/path");
+    std::u16string queryHost(u"query.com");
     result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults);
     EXPECT_EQ(OK, result);
     std::vector<std::string> proxies = string16ToProxyList(kResults);
@@ -196,8 +217,8 @@
     EXPECT_EQ("http.query.com.path.query.com", proxies[0]);
   }
   {
-    String16 queryUrl("ftp://query.com:90/path");
-    String16 queryHost("query.com");
+    std::u16string queryUrl(u"ftp://query.com:90/path");
+    std::u16string queryHost(u"query.com");
     int result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults);
 
     EXPECT_EQ(OK, result);
@@ -219,16 +240,33 @@
   resolver.PurgeMemory();
 }
 
+TEST(ProxyResolverV8Test, Basic_C_API) {
+  auto handle = newProxyResolverV8Handle();
+  int result = ProxyResolverV8Handle_SetPacScript(handle.get(), PASSTHROUGH_JS);
+  EXPECT_EQ(OK, result);
+
+  // The "FindProxyForURL" of this PAC script simply concatenates all of the
+  // arguments into a pseudo-host. The purpose of this test is to verify that
+  // the correct arguments are being passed to FindProxyForURL().
+  std::unique_ptr<char16_t, decltype(&free)> result_list = getProxyForURL(
+      handle.get(), u"http://query.com/path", u"query.com");
+  ASSERT_NE(nullptr, result_list.get());
+  kResults = result_list.get();
+  std::vector<std::string> proxies = string16ToProxyList(kResults);
+  EXPECT_EQ(1U, proxies.size());
+  EXPECT_EQ("http.query.com.path.query.com", proxies[0]);
+}
+
 TEST(ProxyResolverV8Test, BadReturnType) {
   // These are the files of PAC scripts which each return a non-string
   // types for FindProxyForURL(). They should all fail with
   // ERR_PAC_SCRIPT_FAILED.
-  static const String16 files[] = {
-      String16(RETURN_UNDEFINED_JS),
-      String16(RETURN_INTEGER_JS),
-      String16(RETURN_FUNCTION_JS),
-      String16(RETURN_OBJECT_JS),
-      String16(RETURN_NULL_JS)
+  static const std::u16string files[] = {
+      std::u16string(RETURN_UNDEFINED_JS),
+      std::u16string(RETURN_INTEGER_JS),
+      std::u16string(RETURN_FUNCTION_JS),
+      std::u16string(RETURN_OBJECT_JS),
+      std::u16string(RETURN_NULL_JS)
   };
 
   for (size_t i = 0; i < 5; ++i) {
@@ -273,10 +311,20 @@
   EXPECT_EQ(0U, bindings->alerts.size());
 
   // We get one error during compilation.
-  ASSERT_EQ(1U, bindings->errors.size());
+  ASSERT_EQ(2U, bindings->errors.size());
 
   EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
             bindings->errors[0]);
+  EXPECT_EQ("Context is null.", bindings->errors[1]);
+}
+
+TEST(ProxyResolverV8Test, ParseError_C_API) {
+  auto handle = newProxyResolverV8Handle();
+  int result = ProxyResolverV8Handle_SetPacScript(handle.get(), MISSING_CLOSE_BRACE_JS);
+  EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+  std::unique_ptr<char16_t, decltype(&free)> result_list = getProxyForURL(
+      handle.get(), u"http://query.com/path", u"query.com");
+  EXPECT_EQ(nullptr, result_list.get());
 }
 
 // Run a PAC script several times, which has side-effects.
@@ -366,6 +414,9 @@
   // Resolve should fail, as we are not yet initialized with a script.
   int result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
   EXPECT_EQ(ERR_FAILED, result);
+  EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+  EXPECT_EQ(1U, resolver.mock_js_bindings()->errors.size());
+  EXPECT_EQ("Context is null.", resolver.mock_js_bindings()->errors[0]);
 
   // Initialize it.
   result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
@@ -382,6 +433,10 @@
   result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
   EXPECT_EQ(ERR_FAILED, result);
 
+  EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+  EXPECT_EQ(2U, resolver.mock_js_bindings()->errors.size());
+  EXPECT_EQ("Context is null.", resolver.mock_js_bindings()->errors[1]);
+
   // Load a good script once more.
   result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
   EXPECT_EQ(OK, result);
@@ -389,7 +444,7 @@
   EXPECT_EQ(OK, result);
 
   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
-  EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+  EXPECT_EQ(2U, resolver.mock_js_bindings()->errors.size());
 }
 
 // Test marshalling/un-marshalling of values between C++/V8.
@@ -522,7 +577,7 @@
 TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) {
     return;
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
-  int result = resolver.SetPacScript(String16(INTERNATIONAL_DOMAIN_NAMES_JS));
+  int result = resolver.SetPacScript(SCRIPT(INTERNATIONAL_DOMAIN_NAMES_JS));
   EXPECT_EQ(OK, result);
 
   // Execute FindProxyForURL().
@@ -560,5 +615,19 @@
   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
 }
 
+TEST(ProxyResolverV8Test, GetterChangesElementKind) {
+  ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+  int result = resolver.SetPacScript(SCRIPT(CHANGE_ELEMENT_KIND_JS));
+  EXPECT_EQ(OK, result);
+
+  // Execute FindProxyForURL().
+  result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+  EXPECT_EQ(OK, result);
+  std::vector<std::string> proxies = string16ToProxyList(kResults);
+  EXPECT_EQ(1U, proxies.size());
+  EXPECT_EQ("DIRECT", proxies[0]);
+}
+
 }  // namespace
 }  // namespace net
diff --git a/test/proxy_test_script.h b/test/proxy_test_script.h
index bfd953a..092e262 100644
--- a/test/proxy_test_script.h
+++ b/test/proxy_test_script.h
@@ -5,6 +5,7 @@
 #define PROXY_TEST_SCRIPT_H_
 
 #define BINDING_FROM_GLOBAL_JS \
+  u""\
   "// Calls a bindings outside of FindProxyForURL(). This causes the code to\n" \
   "// get exercised during initialization.\n" \
   "\n" \
@@ -15,6 +16,7 @@
   "}\n" \
 
 #define BINDINGS_JS \
+  u""\
   "// Try calling the browser-side bound functions with varying (invalid)\n" \
   "// inputs. There is no notion of \"success\" for this test, other than\n" \
   "// verifying the correct C++ bindings were reached with expected values.\n" \
@@ -79,8 +81,9 @@
   "\n" \
 
 #define CHANGE_ELEMENT_KIND_JS \
+  u""\
   "// PAC script with getter that changes element kind.\n" \
-  "	\n" \
+  "\n" \
   "function FindProxyForURL(url, host) {\n" \
   "  let arr = [];\n" \
   "  arr[1000] = 0x1234;\n" \
@@ -91,17 +94,19 @@
   "  });\n" \
   "\n" \
   "  let results = Object.entries(arr);\n" \
-  "  let str = results.toString(); \n" \
+  "  let str = results.toString();\n" \
   "  return \"DIRECT\";\n" \
   "}\n" \
 
 #define DIRECT_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"DIRECT\";\n" \
   "}\n" \
   "\n" \
 
 #define DNS_FAIL_JS \
+  u""\
   "// This script should be run in an environment where all DNS resolution are\n" \
   "// failing. It tests that functions return the expected values.\n" \
   "//\n" \
@@ -131,6 +136,7 @@
   "\n" \
 
 #define ENDS_WITH_COMMENT_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"PROXY success:80\";\n" \
   "}\n" \
@@ -141,11 +147,13 @@
   "// those extra functions was being considered part of the comment.\n" \
 
 #define ENDS_WITH_STATEMENT_NO_SEMICOLON_JS \
+  u""\
   "// Ends with a statement, and no terminal newline.\n" \
   "function FindProxyForURL(url, host) { return \"PROXY success:\" + x; }\n" \
   "x = 3\n" \
 
 #define INTERNATIONAL_DOMAIN_NAMES_JS \
+  u""\
   "// Try resolving hostnames containing non-ASCII characters.\n" \
   "\n" \
   "function FindProxyForURL(url, host) {\n" \
@@ -164,6 +172,7 @@
   "\n" \
 
 #define MISSING_CLOSE_BRACE_JS \
+  u""\
   "// This PAC script is invalid, because there is a missing close brace\n" \
   "// on the function FindProxyForURL().\n" \
   "\n" \
@@ -172,10 +181,12 @@
   "\n" \
 
 #define NO_ENTRYPOINT_JS \
+  u""\
   "var x = \"This is an invalid PAC script because it lacks a \" +\n" \
   "        \"FindProxyForURL() function\";\n" \
 
 #define PAC_LIBRARY_UNITTEST_JS \
+  u""\
   "// This should output \"PROXY success:80\" if all the tests pass.\n" \
   "// Otherwise it will output \"PROXY failure:<num-failures>\".\n" \
   "//\n" \
@@ -544,6 +555,7 @@
   "\n" \
 
 #define PASSTHROUGH_JS \
+  u""\
   "// Return a single-proxy result, which encodes ALL the arguments that were\n" \
   "// passed to FindProxyForURL().\n" \
   "\n" \
@@ -591,48 +603,56 @@
   "}\n" \
 
 #define RETURN_EMPTY_STRING_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"\";\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_FUNCTION_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return FindProxyForURL;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_INTEGER_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return 0;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_NULL_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return null;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_OBJECT_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return {result: \"PROXY foo\"};\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_UNDEFINED_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return undefined;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_UNICODE_JS \
+  u""\
   "// U+200B is the codepoint for zero-width-space.\n" \
   "function FindProxyForURL(url, host) {\n" \
   "  return \"PROXY foo.com\u200B\";\n" \
   "}\n" \
 
 #define SIDE_EFFECTS_JS \
+  u""\
   "if (!gCounter) {\n" \
   "  // We write it this way so if the script gets loaded twice,\n" \
   "  // gCounter remains dirty.\n" \
@@ -645,6 +665,7 @@
   "\n" \
 
 #define SIMPLE_JS \
+  u""\
   "// PAC script which uses isInNet on both IP addresses and hosts, and calls\n" \
   "// isResolvable().\n" \
   "\n" \
@@ -668,12 +689,13 @@
   "}\n" \
 
 #define STRING_FUNCTIONS_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"direct\".toUpperCase();\n" \
   "}\n" \
-  "\n" \
 
 #define UNHANDLED_EXCEPTION_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  // This will throw a runtime exception.\n" \
   "  return \"PROXY x\" + undefined_variable;\n" \