Update from https://crrev.com/317530

TBR=qsr@chromium.org
BUG=461092

Review URL: https://codereview.chromium.org/952893003
diff --git a/.gitignore b/.gitignore
index a0262de..223119c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,7 +51,6 @@
 /third_party/lss/
 /third_party/mesa/src/
 /third_party/nss/
-/third_party/ots/
 /third_party/pdfium/
 /third_party/pywebsocket/src/
 /third_party/requests/src/
diff --git a/DEPS b/DEPS
index ecc3b17..ab58ade 100644
--- a/DEPS
+++ b/DEPS
@@ -21,19 +21,19 @@
   'chromium_git': 'https://chromium.googlesource.com',
   'dart_svn': 'https://dart.googlecode.com',
   'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
-  'skia_revision': 'd1371a6019189820653aaf20f72ee8f5d0ee3fef',
+  'skia_revision': '792c80f5a7b66e75d42664ccb298f31962c6654c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and V8 without interference from each other.
-  'v8_revision': '055b865a326cdf6a28d2bbb0f197b36e09069fc1',
+  'v8_revision': '3dfd929ea07487f2295553df397720d8d75d227c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '73edef0a2a162e943d5be7715f53bee5d9452b2e',
+  'angle_revision': '6df9b37d8e3aed3aea12058900b7932f911a152a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '5c5e924788fe40f7d6e0a3841ac572de2475e689',
+  'buildtools_revision': '93b3d0af1b30db55ee42bd2e983f7753153217db',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Dart
   # and whatever else without interference from each other.
@@ -45,7 +45,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'd306f165a462f47c2c0e8f3a0f2b4ae6950f70ed',
+  'boringssl_revision': 'b180ee98a60149dd3fd07cce4e834494c9d5b31c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lss
   # and whatever else without interference from each other.
@@ -82,7 +82,7 @@
    Var('chromium_git') + '/angle/angle.git' + '@' +  Var('angle_revision'),
 
   'src/third_party/icu':
-   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '4e3266f32c62d30a3f9e2232a753c60129d1e670',
+   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '2081ee6abfa118003fd559cb72393f5df561dba7',
 
   'src/tools/grit':
     Var('chromium_git') + '/external/grit-i18n.git' + '@' + 'a5890a8118c0c80cc0560e6d8d5cf65e5d725509', # from svn revision 185
@@ -105,9 +105,6 @@
   'src/third_party/skia':
    Var('chromium_git') + '/skia.git' + '@' +  Var('skia_revision'),
 
-  'src/third_party/ots':
-    Var('chromium_git') + '/external/ots.git' + '@' + '98897009f3ea8a5fa3e20a4a74977da7aaa8e61a',
-
   'src/third_party/brotli/src':
    Var('chromium_git') + '/external/font-compression-reference.git' + '@' + '8c9c83426beb4a58da34be76ea1fccb4054c4703',
 
@@ -133,7 +130,7 @@
    'https://boringssl.googlesource.com/boringssl.git' + '@' +  Var('boringssl_revision'),
 
   'src/tools/gyp':
-    Var('chromium_git') + '/external/gyp.git' + '@' + '4d7c139b1820c5fcb993868c61f170a02cda8a40', # from svn revision 2028
+    Var('chromium_git') + '/external/gyp.git' + '@' + '34640080d08ab2a37665512e52142947def3056d', # from svn revision 2028
 }
 
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index e417681..c63d377 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -42,7 +42,6 @@
     "android/jni_android.h",
     "android/jni_array.cc",
     "android/jni_array.h",
-    "android/jni_onload_delegate.h",
     "android/jni_registrar.cc",
     "android/jni_registrar.h",
     "android/jni_string.cc",
@@ -676,6 +675,10 @@
     "trace_event/memory_dump_provider.h",
     "trace_event/process_memory_dump.cc",
     "trace_event/process_memory_dump.h",
+    "trace_event/process_memory_totals.cc",
+    "trace_event/process_memory_totals.h",
+    "trace_event/process_memory_totals_dump_provider.cc",
+    "trace_event/process_memory_totals_dump_provider.h",
     "trace_event/trace_event.h",
     "trace_event/trace_event_android.cc",
     "trace_event/trace_event_argument.cc",
@@ -869,7 +872,7 @@
     ]
 
     # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
     libs = [
       "cfgmgr32.lib",
@@ -1028,10 +1031,8 @@
     configs += [ "//build/config/compiler:optimize_max" ]
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 source_set("prefs") {
@@ -1335,6 +1336,7 @@
     "timer/timer_unittest.cc",
     "tools_sanity_unittest.cc",
     "trace_event/memory_dump_manager_unittest.cc",
+    "trace_event/process_memory_totals_dump_provider_unittest.cc",
     "trace_event/trace_event_argument_unittest.cc",
     "trace_event/trace_event_memory_unittest.cc",
     "trace_event/trace_event_synthetic_delay_unittest.cc",
@@ -1439,10 +1441,8 @@
     set_sources_assignment_filter(sources_assignment_filter)
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 if (is_android) {
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index e931b1c..a07a356 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -36,7 +36,7 @@
     args = [
       visual_studio_path + "/vc/lib",
       rebase_path("$target_gen_dir/allocator"),
-      cpu_arch,
+      current_cpu,
     ]
   }
 }
diff --git a/base/android/base_jni_onload.cc b/base/android/base_jni_onload.cc
index ae64120..c3a65d4 100644
--- a/base/android/base_jni_onload.cc
+++ b/base/android/base_jni_onload.cc
@@ -5,59 +5,51 @@
 #include "base/android/base_jni_onload.h"
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/android/jni_utils.h"
 #include "base/android/library_loader/library_loader_hooks.h"
+#include "base/bind.h"
 
 namespace base {
 namespace android {
 
 namespace {
 
-// The JNIOnLoadDelegate implementation in base.
-class BaseJNIOnLoadDelegate : public JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool BaseJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return RegisterLibraryLoaderEntryHook(env);
 }
 
-bool BaseJNIOnLoadDelegate::Init() {
+bool Init() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::InitReplacementClassLoader(env,
+                                            base::android::GetClassLoader(env));
   return true;
 }
 
 }  // namespace
 
 
-bool OnJNIOnLoad(JavaVM* vm,
-                 std::vector<JNIOnLoadDelegate*>* delegates) {
+bool OnJNIOnLoadRegisterJNI(JavaVM* vm,
+                            std::vector<RegisterCallback> callbacks) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
 
-  BaseJNIOnLoadDelegate delegate;
-  delegates->push_back(&delegate);
-  bool ret = true;
-  for (std::vector<JNIOnLoadDelegate*>::reverse_iterator i =
-           delegates->rbegin(); i != delegates->rend(); ++i) {
-    if (!(*i)->RegisterJNI(env)) {
-      ret = false;
-      break;
-    }
+  callbacks.push_back(base::Bind(&RegisterJNI));
+  for (std::vector<RegisterCallback>::reverse_iterator i =
+           callbacks.rbegin(); i != callbacks.rend(); ++i) {
+    if (!i->Run(env))
+      return false;
   }
+  return true;
+}
 
-  if (ret) {
-    for (std::vector<JNIOnLoadDelegate*>::reverse_iterator i =
-             delegates->rbegin(); i != delegates->rend(); ++i) {
-      if (!(*i)->Init()) {
-        ret = false;
-        break;
-      }
-    }
+bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks) {
+  callbacks.push_back(base::Bind(&Init));
+  for (std::vector<InitCallback>::reverse_iterator i =
+           callbacks.rbegin(); i != callbacks.rend(); ++i) {
+    if (!i->Run())
+      return false;
   }
-  delegates->pop_back();
-  return ret;
+  return true;
 }
 
 }  // namespace android
diff --git a/base/android/base_jni_onload.h b/base/android/base_jni_onload.h
index f3f05fa..dcc7756 100644
--- a/base/android/base_jni_onload.h
+++ b/base/android/base_jni_onload.h
@@ -9,17 +9,22 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/callback.h"
 
 namespace base {
 namespace android {
 
-class JNIOnLoadDelegate;
+// Returns whether JNI registration succeeded. Caller shall put the
+// RegisterCallback into |callbacks| in reverse order.
+typedef base::Callback<bool(JNIEnv*)> RegisterCallback;
+BASE_EXPORT bool OnJNIOnLoadRegisterJNI(
+    JavaVM* vm,
+    std::vector<RegisterCallback> callbacks);
 
-// Returns whether JNI registration and initialization succeeded. Caller shall
-// put the JNIOnLoadDelegate into |delegates| in reverse order. Refer
-// JNIOnLoadDelegate for more information.
-BASE_EXPORT bool OnJNIOnLoad(JavaVM* vm,
-                             std::vector<JNIOnLoadDelegate*>* delegates);
+// Returns whether initialization succeeded. Caller shall put the
+// InitCallback into |callbacks| in reverse order.
+typedef base::Callback<bool(void)> InitCallback;
+BASE_EXPORT bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks);
 
 }  // namespace android
 }  // namespace base
diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java
index 9252b4d..d44f2fc 100644
--- a/base/android/java/src/org/chromium/base/ResourceExtractor.java
+++ b/base/android/java/src/org/chromium/base/ResourceExtractor.java
@@ -12,6 +12,8 @@
 import android.content.res.AssetManager;
 import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Trace;
 import android.preference.PreferenceManager;
 import android.util.Log;
@@ -57,6 +59,8 @@
     private class ExtractTask extends AsyncTask<Void, Void, Void> {
         private static final int BUFFER_SIZE = 16 * 1024;
 
+        private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
+
         public ExtractTask() {
         }
 
@@ -211,6 +215,23 @@
             return null;
         }
 
+        private void onPostExecuteImpl() {
+            for (int i = 0; i < mCompletionCallbacks.size(); i++) {
+                mCompletionCallbacks.get(i).run();
+            }
+            mCompletionCallbacks.clear();
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            beginTraceSection("ResourceExtractor.ExtractTask.onPostExecute");
+            try {
+                onPostExecuteImpl();
+            } finally {
+                endTraceSection();
+            }
+        }
+
         // Looks for a timestamp file on disk that indicates the version of the APK that
         // the resource paks were extracted from. Returns null if a timestamp was found
         // and it indicates that the resources match the current APK. Otherwise returns
@@ -335,6 +356,13 @@
         mContext = context.getApplicationContext();
     }
 
+    /**
+     * Synchronously wait for the resource extraction to be completed.
+     * <p>
+     * This method is bad and you should feel bad for using it.
+     *
+     * @see #addCompletionCallback(Runnable)
+     */
     public void waitForCompletion() {
         if (shouldSkipPakExtraction()) {
             return;
@@ -355,6 +383,35 @@
     }
 
     /**
+     * Adds a callback to be notified upon the completion of resource extraction.
+     * <p>
+     * If the resource task has already completed, the callback will be posted to the UI message
+     * queue.  Otherwise, it will be executed after all the resources have been extracted.
+     * <p>
+     * This must be called on the UI thread.  The callback will also always be executed on
+     * the UI thread.
+     *
+     * @param callback The callback to be enqueued.
+     */
+    public void addCompletionCallback(Runnable callback) {
+        ThreadUtils.assertOnUiThread();
+
+        Handler handler = new Handler(Looper.getMainLooper());
+        if (shouldSkipPakExtraction()) {
+            handler.post(callback);
+            return;
+        }
+
+        assert mExtractTask != null;
+        assert !mExtractTask.isCancelled();
+        if (mExtractTask.getStatus() == AsyncTask.Status.FINISHED) {
+            handler.post(callback);
+        } else {
+            mExtractTask.mCompletionCallbacks.add(callback);
+        }
+    }
+
+    /**
      * This will extract the application pak resources in an
      * AsyncTask. Call waitForCompletion() at the point resources
      * are needed to block until the task completes.
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 23f953c..bbf76cb 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -1039,7 +1039,7 @@
             mLoadSize = in.readLong();
             mRelroStart = in.readLong();
             mRelroSize = in.readLong();
-            ParcelFileDescriptor fd = in.readFileDescriptor();
+            ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
             // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null.
             mRelroFd = (fd == null) ? -1 : fd.detachFd();
         }
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc
index e09c2d5..a2de00a 100644
--- a/base/android/jni_android.cc
+++ b/base/android/jni_android.cc
@@ -17,6 +17,8 @@
 using base::android::MethodID;
 using base::android::ScopedJavaLocalRef;
 
+bool g_disable_manual_jni_registration = false;
+
 JavaVM* g_jvm = NULL;
 // Leak the global app context, as it is used from a non-joinable worker thread
 // that may still be running at shutdown. There is no harm in doing this.
@@ -77,6 +79,15 @@
 namespace base {
 namespace android {
 
+bool IsManualJniRegistrationDisabled() {
+  return g_disable_manual_jni_registration;
+}
+
+void DisableManualJniRegistration() {
+  DCHECK(!g_disable_manual_jni_registration);
+  g_disable_manual_jni_registration = true;
+}
+
 JNIEnv* AttachCurrentThread() {
   DCHECK(g_jvm);
   JNIEnv* env = NULL;
diff --git a/base/android/jni_android.h b/base/android/jni_android.h
index b5e5526..504eb85 100644
--- a/base/android/jni_android.h
+++ b/base/android/jni_android.h
@@ -21,6 +21,13 @@
 // Used to mark symbols to be exported in a shared library's symbol table.
 #define JNI_EXPORT __attribute__ ((visibility("default")))
 
+// Used to disable manual JNI registration in binaries that prefer to use native
+// JNI exports for startup performance. This is not compatible with the crazy
+// linker and so defaults to off. Call DisableManualJniRegistration at the very
+// beginning of JNI_OnLoad to use this.
+BASE_EXPORT bool IsManualJniRegistrationDisabled();
+BASE_EXPORT void DisableManualJniRegistration();
+
 // Contains the registration method information for initializing JNI bindings.
 struct RegistrationMethod {
   const char* name;
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 6e39c13..54fea6b 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -889,7 +889,7 @@
 
   def GetJNINativeMethodsString(self):
     """Returns the implementation of the array of native methods."""
-    if self.options.native_exports:
+    if self.options.native_exports and not self.options.native_exports_optional:
       return ''
     template = Template("""\
 static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
@@ -922,7 +922,7 @@
     """Returns the code for RegisterNatives."""
     template = Template("""\
 ${REGISTER_NATIVES_SIGNATURE} {
-${CLASSES}
+${EARLY_EXIT}${CLASSES}
 ${NATIVES}
 ${CALLED_BY_NATIVES}
   return true;
@@ -934,9 +934,16 @@
     else:
       signature += ')'
 
+    early_exit = ''
+    if self.options.native_exports_optional:
+      early_exit = """\
+  if (base::android::IsManualJniRegistrationDisabled()) return true;
+"""
+
     natives = self.GetRegisterNativesImplString()
     called_by_natives = self.GetRegisterCalledByNativesImplString()
     values = {'REGISTER_NATIVES_SIGNATURE': signature,
+              'EARLY_EXIT': early_exit,
               'CLASSES': self.GetFindClasses(),
               'NATIVES': natives,
               'CALLED_BY_NATIVES': called_by_natives,
@@ -945,7 +952,7 @@
 
   def GetRegisterNativesImplString(self):
     """Returns the shared implementation for RegisterNatives."""
-    if self.options.native_exports:
+    if self.options.native_exports and not self.options.native_exports_optional:
       return ''
 
     template = Template("""\
@@ -1035,6 +1042,31 @@
         param.name
         for param in called_by_native.params])
 
+  def GetStubName(self, native):
+    """Return the name of the stub function for this native method.
+
+    Args:
+      native: the native dictionary describing the method.
+
+    Returns:
+      A string with the stub function name. For native exports mode this is the
+      Java_* symbol name required by the JVM; otherwise it is just the name of
+      the native method itself.
+    """
+    if self.options.native_exports:
+      template = Template("Java_${JAVA_NAME}_native${NAME}")
+
+      java_name = JniParams.RemapClassName(self.fully_qualified_class)
+      java_name = java_name.replace('_', '_1').replace('/', '_')
+      if native.java_class_name:
+        java_name += '_00024' + native.java_class_name
+
+      values = {'NAME': native.name,
+                'JAVA_NAME': java_name}
+      return template.substitute(values)
+    else:
+      return native.name
+
   def GetForwardDeclaration(self, native):
     template_str = """
 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
@@ -1042,7 +1074,7 @@
     if self.options.native_exports:
       template_str += """
 __attribute__((visibility("default"), alias("${NAME}")))
-${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env, ${PARAMS});
+${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS});
 """
     template = Template(template_str)
     params_in_call = []
@@ -1050,16 +1082,11 @@
       params_in_call = ['env', 'jcaller']
     params_in_call = ', '.join(params_in_call + [p.name for p in native.params])
 
-    java_name = JniParams.RemapClassName(self.fully_qualified_class)
-    java_name = java_name.replace('_', '_1').replace('/', '_')
-    if native.java_class_name:
-      java_name += '_00024' + native.java_class_name
-
     values = {'RETURN': JavaDataTypeToC(native.return_type),
               'NAME': native.name,
-              'JAVA_NAME': java_name,
               'PARAMS': self.GetParamsInDeclaration(native),
-              'PARAMS_IN_CALL': params_in_call}
+              'PARAMS_IN_CALL': params_in_call,
+              'STUB_NAME': self.GetStubName(native)}
     return template.substitute(values)
 
   def GetNativeMethodStubString(self, native):
@@ -1067,11 +1094,11 @@
     if self.options.native_exports:
       template_str = """\
 __attribute__((visibility("default")))
-${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env,
+${RETURN} ${STUB_NAME}(JNIEnv* env,
     ${PARAMS_IN_DECLARATION}) {"""
     else:
       template_str = """\
-static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
+static ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
     template_str += """
   ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
   CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
@@ -1093,24 +1120,16 @@
     if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
       post_call = '.Release()'
 
-    if self.options.native_exports:
-      java_name = JniParams.RemapClassName(self.fully_qualified_class)
-      java_name = java_name.replace('_', '_1').replace('/', '_')
-      if native.java_class_name:
-        java_name += '_00024' + native.java_class_name
-    else:
-      java_name = ''
-
     values = {
         'RETURN': return_type,
         'OPTIONAL_ERROR_RETURN': optional_error_return,
-        'JAVA_NAME': java_name,
         'NAME': native.name,
         'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
         'PARAM0_NAME': native.params[0].name,
         'P0_TYPE': native.p0_type,
         'PARAMS_IN_CALL': params_in_call,
-        'POST_CALL': post_call
+        'POST_CALL': post_call,
+        'STUB_NAME': self.GetStubName(native),
     }
     return template.substitute(values)
 
@@ -1225,12 +1244,13 @@
     return template.substitute(values)
 
   def GetKMethodArrayEntry(self, native):
-    template = Template("""\
-    { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
+    template = Template('    { "native${NAME}", ${JNI_SIGNATURE}, ' +
+                        'reinterpret_cast<void*>(${STUB_NAME}) },')
     values = {'NAME': native.name,
               'JNI_SIGNATURE': JniParams.Signature(native.params,
                                                    native.return_type,
-                                                   True)}
+                                                   True),
+              'STUB_NAME': self.GetStubName(native)}
     return template.substitute(values)
 
   def GetUniqueClasses(self, origin):
@@ -1500,7 +1520,12 @@
   option_parser.add_option('--native_exports', action='store_true',
                            help='Native method registration through .so '
                            'exports.')
+  option_parser.add_option('--native_exports_optional', action='store_true',
+                           help='Support both explicit and native method'
+                           'registration.')
   options, args = option_parser.parse_args(argv)
+  if options.native_exports_optional:
+    options.native_exports = True
   if options.jar_file:
     input_file = ExtractJarInputFile(options.jar_file, options.input_file,
                                      options.output_dir)
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 7e39cda..e29bc0c 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -43,6 +43,7 @@
     self.cpp = 'cpp'
     self.javap = 'javap'
     self.native_exports = False
+    self.native_exports_optional = False
 
 class TestGenerator(unittest.TestCase):
   def assertObjEquals(self, first, second):
@@ -1019,7 +1020,7 @@
         test_data, 'org/chromium/example/jni_generator/Test', options)
     self.assertGoldenTextEquals(jni_from_java.GetContent())
 
-  def testNativeExportsOption(self):
+  def runNativeExportsOption(self, optional):
     test_data = """
     package org.chromium.example.jni_generator;
 
@@ -1054,9 +1055,18 @@
     options = TestOptions()
     options.jni_init_native_name = 'nativeInitNativeClass'
     options.native_exports = True
+    options.native_exports_optional = optional
     jni_from_java = jni_generator.JNIFromJavaSource(
         test_data, 'org/chromium/example/jni_generator/SampleForTests', options)
-    self.assertGoldenTextEquals(jni_from_java.GetContent())
+    return jni_from_java.GetContent()
+
+  def testNativeExportsOption(self):
+    content = self.runNativeExportsOption(False)
+    self.assertGoldenTextEquals(content)
+
+  def testNativeExportsOptionalOption(self):
+    content = self.runNativeExportsOption(True)
+    self.assertGoldenTextEquals(content)
 
   def testOuterInnerRaises(self):
     test_data = """
diff --git a/base/android/jni_generator/testNativeExportsOptionalOption.golden b/base/android/jni_generator/testNativeExportsOptionalOption.golden
new file mode 100644
index 0000000..2a3b172
--- /dev/null
+++ b/base/android/jni_generator/testNativeExportsOptionalOption.golden
@@ -0,0 +1,283 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kSampleForTestsClassPath[] =
+    "org/chromium/example/jni_generator/SampleForTests";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_SampleForTests_clazz __attribute__((unused)) = 0;
+#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz)
+
+}  // namespace
+
+extern "C" {
+
+static jint Init(JNIEnv* env, jobject jcaller);
+
+__attribute__((visibility("default"), alias("Init")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
+    env, jobject jcaller);
+
+static jint Init(JNIEnv* env, jobject jcaller);
+
+__attribute__((visibility("default"), alias("Init")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
+    env, jobject jcaller);
+
+};  // extern "C"
+
+// Step 2: method stubs.
+
+extern "C" {
+__attribute__((visibility("default")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(JNIEnv*
+    env,
+    jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
+  return native->StaticMethod(env, jcaller, arg1);
+}
+
+__attribute__((visibility("default")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv*
+    env,
+    jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
+  return native->Method(env, jcaller, arg1);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
+static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, jobject obj,
+    JniIntWrapper iParam) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, obj,
+      SampleForTests_clazz(env));
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+      env, SampleForTests_clazz(env),
+      "testMethodWithParam",
+
+"("
+"I"
+")"
+"V",
+      &g_SampleForTests_testMethodWithParam);
+
+     env->CallVoidMethod(obj,
+          method_id, as_jint(iParam));
+  jni_generator::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithParamAndReturn =
+    0;
+static base::android::ScopedJavaLocalRef<jstring>
+    Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, jobject obj,
+    JniIntWrapper iParam) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, obj,
+      SampleForTests_clazz(env), NULL);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+      env, SampleForTests_clazz(env),
+      "testMethodWithParamAndReturn",
+
+"("
+"I"
+")"
+"Ljava/lang/String;",
+      &g_SampleForTests_testMethodWithParamAndReturn);
+
+  jstring ret =
+      static_cast<jstring>(env->CallObjectMethod(obj,
+          method_id, as_jint(iParam)));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithParam = 0;
+static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env,
+    JniIntWrapper iParam) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, SampleForTests_clazz(env),
+      SampleForTests_clazz(env), 0);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+      env, SampleForTests_clazz(env),
+      "testStaticMethodWithParam",
+
+"("
+"I"
+")"
+"I",
+      &g_SampleForTests_testStaticMethodWithParam);
+
+  jint ret =
+      env->CallStaticIntMethod(SampleForTests_clazz(env),
+          method_id, as_jint(iParam));
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0;
+static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, SampleForTests_clazz(env),
+      SampleForTests_clazz(env), 0);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+      env, SampleForTests_clazz(env),
+      "testMethodWithNoParam",
+
+"("
+")"
+"D",
+      &g_SampleForTests_testMethodWithNoParam);
+
+  jdouble ret =
+      env->CallStaticDoubleMethod(SampleForTests_clazz(env),
+          method_id);
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithNoParam =
+    0;
+static base::android::ScopedJavaLocalRef<jstring>
+    Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, SampleForTests_clazz(env),
+      SampleForTests_clazz(env), NULL);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+      env, SampleForTests_clazz(env),
+      "testStaticMethodWithNoParam",
+
+"("
+")"
+"Ljava/lang/String;",
+      &g_SampleForTests_testStaticMethodWithNoParam);
+
+  jstring ret =
+static_cast<jstring>(env->CallStaticObjectMethod(SampleForTests_clazz(env),
+          method_id));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+};  // extern "C"
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+    { "nativeInit",
+"("
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit)
+    },
+};
+
+static const JNINativeMethod kMethodsMyInnerClass[] = {
+    { "nativeInit",
+"("
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit)
+    },
+};
+
+static const JNINativeMethod kMethodsSampleForTests[] = {
+    { "nativeStaticMethod",
+"("
+"J"
+"I"
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod)
+    },
+    { "nativeMethod",
+"("
+"J"
+"I"
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod)
+    },
+};
+
+static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
+  if (base::android::IsManualJniRegistrationDisabled()) return true;
+    base::subtle::Release_Store(&g_SampleForTests_clazz,
+      static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));
+
+  const int kMethodsMyOtherInnerClassSize =
+      arraysize(kMethodsMyOtherInnerClass);
+
+  if (env->RegisterNatives(MyOtherInnerClass_clazz(env),
+                           kMethodsMyOtherInnerClass,
+                           kMethodsMyOtherInnerClassSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, MyOtherInnerClass_clazz(env), __FILE__);
+    return false;
+  }
+
+  const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+  if (env->RegisterNatives(MyInnerClass_clazz(env),
+                           kMethodsMyInnerClass,
+                           kMethodsMyInnerClassSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, MyInnerClass_clazz(env), __FILE__);
+    return false;
+  }
+
+  const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
+
+  if (env->RegisterNatives(SampleForTests_clazz(env),
+                           kMethodsSampleForTests,
+                           kMethodsSampleForTestsSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, SampleForTests_clazz(env), __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+extern "C" JNIEXPORT bool JNICALL
+Java_org_chromium_example_jni_1generator_SampleForTests_nativeInitNativeClass(JNIEnv*
+    env, jclass clazz) {
+  return RegisterNativesImpl(env, clazz);
+}
+
+#endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_onload_delegate.h b/base/android/jni_onload_delegate.h
deleted file mode 100644
index ef1b137..0000000
--- a/base/android/jni_onload_delegate.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
-#define BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
-
-#include <jni.h>
-
-#include "base/base_export.h"
-
-namespace base {
-namespace android {
-
-// This delegate class is used to implement component specific JNI registration
-// and initialization. All methods are called in JNI_OnLoad().
-//
-// Both RegisterJNI() and Init() methods are called if the shared library
-// is loaded by crazy linker that can't find JNI methods without JNI
-// registration, otherwise, only Init() is invoked where dynamic lookup is
-// used to find the JNI methods.
-//
-// It is important to make sure the JNI registration code is only in
-// RegisterJNI(), so it could be stripped out when JNI registration isn't
-// needed.
-class BASE_EXPORT JNIOnLoadDelegate {
- public:
-  virtual ~JNIOnLoadDelegate() {}
-
-  // Returns whether the JNI registration succeeded.
-  virtual bool RegisterJNI(JNIEnv* env) = 0;
-
-  // Returns whether the initialization succeeded. This method is called after
-  // RegisterJNI(), all JNI methods shall ready to be used.
-  virtual bool Init() = 0;
-};
-
-}  // namespace android
-}  // namespace base
-
-#endif  // BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
diff --git a/base/base.gyp b/base/base.gyp
index 8cd5d6a..213e62d 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -645,6 +645,7 @@
         'timer/timer_unittest.cc',
         'tools_sanity_unittest.cc',
         'trace_event/memory_dump_manager_unittest.cc',
+        'trace_event/process_memory_totals_dump_provider_unittest.cc',
         'trace_event/trace_event_argument_unittest.cc',
         'trace_event/trace_event_memory_unittest.cc',
         'trace_event/trace_event_synthetic_delay_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 37a8171..b7c33b8 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -44,7 +44,6 @@
           'android/jni_android.h',
           'android/jni_array.cc',
           'android/jni_array.h',
-          'android/jni_onload_delegate.h',
           'android/jni_registrar.cc',
           'android/jni_registrar.h',
           'android/jni_string.cc',
@@ -672,6 +671,10 @@
           'trace_event/memory_dump_provider.h',
           'trace_event/process_memory_dump.cc',
           'trace_event/process_memory_dump.h',
+          'trace_event/process_memory_totals.cc',
+          'trace_event/process_memory_totals.h',
+          'trace_event/process_memory_totals_dump_provider.cc',
+          'trace_event/process_memory_totals_dump_provider.h',
           'trace_event/trace_event.h',
           'trace_event/trace_event_android.cc',
           'trace_event/trace_event_argument.cc',
diff --git a/base/i18n/break_iterator.cc b/base/i18n/break_iterator.cc
index e3aaa2b..e2ed667 100644
--- a/base/i18n/break_iterator.cc
+++ b/base/i18n/break_iterator.cc
@@ -74,7 +74,8 @@
                       static_cast<int32_t>(string_.size()),
                       &status);
     if (U_FAILURE(status)) {
-      NOTREACHED() << "ubrk_open failed";
+      NOTREACHED() << "ubrk_open failed for type " << break_type
+          << " with error " << status;
     }
   }
 
diff --git a/base/i18n/time_formatting_unittest.cc b/base/i18n/time_formatting_unittest.cc
index 4739b62..df0c1ed 100644
--- a/base/i18n/time_formatting_unittest.cc
+++ b/base/i18n/time_formatting_unittest.cc
@@ -158,11 +158,11 @@
 
   EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time));
   EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time));
-  EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07"),
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"),
             TimeFormatShortDateAndTime(time));
-  EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07 ") + GetShortTimeZone(),
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(),
             TimeFormatShortDateAndTimeWithTimeZone(time));
-  EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 15:42:07"),
+  EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"),
             TimeFormatFriendlyDateAndTime(time));
   EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"),
             TimeFormatFriendlyDate(time));
diff --git a/base/ios/device_util.mm b/base/ios/device_util.mm
index ff7be36..1234562 100644
--- a/base/ios/device_util.mm
+++ b/base/ios/device_util.mm
@@ -13,7 +13,6 @@
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 
-#include "base/ios/ios_util.h"
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/memory/scoped_ptr.h"
diff --git a/base/ios/device_util_unittest.mm b/base/ios/device_util_unittest.mm
index 3494e00..82d4217 100644
--- a/base/ios/device_util_unittest.mm
+++ b/base/ios/device_util_unittest.mm
@@ -5,7 +5,6 @@
 #import <UIKit/UIKit.h>
 
 #include "base/ios/device_util.h"
-#include "base/ios/ios_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc
index 27b390f..5dcdfdc 100644
--- a/base/memory/discardable_memory_android.cc
+++ b/base/memory/discardable_memory_android.cc
@@ -53,8 +53,8 @@
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
     DISCARDABLE_MEMORY_TYPE_ASHMEM,
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
diff --git a/base/memory/discardable_memory_linux.cc b/base/memory/discardable_memory_linux.cc
index 977b029..670ad7e 100644
--- a/base/memory/discardable_memory_linux.cc
+++ b/base/memory/discardable_memory_linux.cc
@@ -19,8 +19,8 @@
 void DiscardableMemory::GetSupportedTypes(
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
diff --git a/base/memory/discardable_memory_mac.cc b/base/memory/discardable_memory_mac.cc
index c8669a6..e0096e5 100644
--- a/base/memory/discardable_memory_mac.cc
+++ b/base/memory/discardable_memory_mac.cc
@@ -23,8 +23,8 @@
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
     DISCARDABLE_MEMORY_TYPE_MACH,
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
diff --git a/base/memory/discardable_memory_win.cc b/base/memory/discardable_memory_win.cc
index 977b029..670ad7e 100644
--- a/base/memory/discardable_memory_win.cc
+++ b/base/memory/discardable_memory_win.cc
@@ -19,8 +19,8 @@
 void DiscardableMemory::GetSupportedTypes(
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
diff --git a/base/memory/scoped_ptr_unittest.cc b/base/memory/scoped_ptr_unittest.cc
index 0887a99..766f444 100644
--- a/base/memory/scoped_ptr_unittest.cc
+++ b/base/memory/scoped_ptr_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -686,8 +685,11 @@
 // value first.
 TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) {
   scoped_ptr<int> x(new int);
-  std::stringstream s;
-  s << x;
-  std::string expected = base::StringPrintf("%p", x.get());
-  EXPECT_EQ(expected, s.str());
+  std::stringstream s1;
+  s1 << x;
+
+  std::stringstream s2;
+  s2 << x.get();
+
+  EXPECT_EQ(s2.str(), s1.str());
 }
diff --git a/base/memory/singleton.h b/base/memory/singleton.h
index e5e2e3e..e50bdc0 100644
--- a/base/memory/singleton.h
+++ b/base/memory/singleton.h
@@ -23,7 +23,6 @@
 #include "base/atomicops.h"
 #include "base/base_export.h"
 #include "base/memory/aligned_memory.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/thread_restrictions.h"
 
 namespace base {
@@ -237,8 +236,6 @@
     // instance_ pointer must acquire visibility over the singleton data.
     base::subtle::AtomicWord value = base::subtle::Acquire_Load(&instance_);
     if (value != 0 && value != base::internal::kBeingCreatedMarker) {
-      // See the corresponding HAPPENS_BEFORE below.
-      ANNOTATE_HAPPENS_AFTER(&instance_);
       return reinterpret_cast<Type*>(value);
     }
 
@@ -250,10 +247,6 @@
       // stop right after we do this store.
       Type* newval = Traits::New();
 
-      // This annotation helps race detectors recognize correct lock-less
-      // synchronization between different threads calling get().
-      // See the corresponding HAPPENS_AFTER below and above.
-      ANNOTATE_HAPPENS_BEFORE(&instance_);
       // Releases the visibility over instance_ to the readers.
       base::subtle::Release_Store(
           &instance_, reinterpret_cast<base::subtle::AtomicWord>(newval));
@@ -267,8 +260,6 @@
     // We hit a race. Wait for the other thread to complete it.
     value = base::internal::WaitForInstance(&instance_);
 
-    // See the corresponding HAPPENS_BEFORE above.
-    ANNOTATE_HAPPENS_AFTER(&instance_);
     return reinterpret_cast<Type*>(value);
   }
 
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 4e600f9..8e5360b 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
 #include "base/process/kill.h"
 #include "base/win/windows_version.h"
 
@@ -165,7 +166,21 @@
     priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
                        PROCESS_MODE_BACKGROUND_END;
   } else {
-    priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+    // Experiment (http://crbug.com/458594) with using IDLE_PRIORITY_CLASS as a
+    // background priority for background renderers (this code path is
+    // technically for more than just the renderers but they're the only use
+    // case in practice and experimenting here direclty is thus easier -- plus
+    // it doesn't really hurt as above we already state our intent of using
+    // PROCESS_MODE_BACKGROUND_BEGIN if available which is essentially
+    // IDLE_PRIORITY_CLASS plus lowered IO priority). Enabled by default in the
+    // asbence of field trials to get coverage on the perf waterfall.
+    DWORD background_priority = IDLE_PRIORITY_CLASS;
+    base::FieldTrial* trial =
+        base::FieldTrialList::Find("BackgroundRendererProcesses");
+    if (trial && trial->group_name() == "AllowBelowNormalFromBrowser")
+      background_priority = BELOW_NORMAL_PRIORITY_CLASS;
+
+    priority = value ? background_priority : NORMAL_PRIORITY_CLASS;
   }
 
   return (::SetPriorityClass(Handle(), priority) != 0);
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
index b6c103d..a104831 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
@@ -4,19 +4,20 @@
 
 package org.chromium.base.test;
 
-import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
 import android.test.AndroidTestRunner;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
 
-import junit.framework.Test;
 import junit.framework.TestCase;
-import junit.framework.TestSuite;
+import junit.framework.TestResult;
 
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  *  An Instrumentation test runner that checks SDK level for tests with specific requirements.
  */
@@ -24,40 +25,94 @@
 
     private static final String TAG = "BaseInstrumentationTestRunner";
 
-    @Override
-    protected AndroidTestRunner getAndroidTestRunner() {
-        return new BaseAndroidTestRunner(getContext());
+    /**
+     * An interface for classes that check whether a test case should be skipped.
+     */
+    public interface SkipCheck {
+        /**
+         * Checks whether the given test case should be skipped.
+         *
+         * @param testCase The test case to check.
+         * @return Whether the test case should be skipped.
+         */
+        public boolean shouldSkip(TestCase testCase);
     }
 
     /**
-     *  Skips tests that don't meet the requirements of the current device.
+     * A test result that can skip tests.
      */
-    public class BaseAndroidTestRunner extends AndroidTestRunner {
-        private final Context mContext;
+    public class SkippingTestResult extends TestResult {
 
-        public BaseAndroidTestRunner(Context context) {
-            mContext = context;
+        private final List<SkipCheck> mSkipChecks;
+
+        /**
+         * Creates an instance of SkippingTestResult.
+         */
+        public SkippingTestResult() {
+            mSkipChecks = new ArrayList<SkipCheck>();
+        }
+
+        /**
+         * Adds a check for whether a test should run.
+         *
+         * @param skipCheck The check to add.
+         */
+        public void addSkipCheck(SkipCheck skipCheck) {
+            mSkipChecks.add(skipCheck);
+        }
+
+        private boolean shouldSkip(final TestCase test) {
+            for (SkipCheck s : mSkipChecks) {
+                if (s.shouldSkip(test)) return true;
+            }
+            return false;
         }
 
         @Override
-        public void setTest(Test test) {
-            super.setTest(test);
-            TestSuite revisedTestSuite = new TestSuite();
-            for (TestCase testCase : this.getTestCases()) {
-                Class<?> testClass = testCase.getClass();
-                if (shouldSkip(testClass, testCase)) {
-                    revisedTestSuite.addTest(new SkippedTest(testCase));
-                    Bundle skipResult = new Bundle();
-                    skipResult.putBoolean("test_skipped", true);
-                    sendStatus(0, skipResult);
-                } else {
-                    revisedTestSuite.addTest(testCase);
-                }
-            }
-            super.setTest(revisedTestSuite);
-        }
+        protected void run(final TestCase test) {
+            if (shouldSkip(test)) {
+                startTest(test);
 
-        protected boolean shouldSkip(Class<?> testClass, TestCase testCase) {
+                Bundle skipResult = new Bundle();
+                skipResult.putString("class", test.getClass().getName());
+                skipResult.putString("test", test.getName());
+                skipResult.putBoolean("test_skipped", true);
+                sendStatus(0, skipResult);
+
+                endTest(test);
+            } else {
+                super.run(test);
+            }
+        }
+    }
+
+    @Override
+    protected AndroidTestRunner getAndroidTestRunner() {
+        return new AndroidTestRunner() {
+            @Override
+            protected TestResult createTestResult() {
+                SkippingTestResult r = new SkippingTestResult();
+                r.addSkipCheck(new MinAndroidSdkLevelSkipCheck());
+                return r;
+            }
+        };
+    }
+
+    /**
+     * Checks the device's SDK level against any specified minimum requirement.
+     */
+    public static class MinAndroidSdkLevelSkipCheck implements SkipCheck {
+
+        /**
+         * If {@link org.chromium.base.test.util.MinAndroidSdkLevel} is present, checks its value
+         * against the device's SDK level.
+         *
+         * @param testCase The test to check.
+         * @return true if the device's SDK level is below the specified minimum.
+         */
+        @Override
+        public boolean shouldSkip(TestCase testCase) {
+            Class<?> testClass = testCase.getClass();
             if (testClass.isAnnotationPresent(MinAndroidSdkLevel.class)) {
                 MinAndroidSdkLevel v = testClass.getAnnotation(MinAndroidSdkLevel.class);
                 if (Build.VERSION.SDK_INT < v.value()) {
@@ -69,28 +124,6 @@
             }
             return false;
         }
-
-        protected Context getContext() {
-            return mContext;
-        }
     }
 
-    /**
-     *  Replaces a TestCase that should be skipped.
-     */
-    public static class SkippedTest extends TestCase {
-
-        public SkippedTest(TestCase skipped) {
-            super(skipped.getClass().getName() + "#" + skipped.getName());
-        }
-
-        @Override
-        protected void runTest() throws Throwable {
-        }
-
-        @Override
-        public String toString() {
-            return "SKIPPED " + super.toString();
-        }
-    }
 }
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index 903e93e..d40dd98 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -35,6 +35,14 @@
 #endif  // OS_IOS
 #endif  // OS_MACOSX
 
+#if !defined(OS_WIN)
+#include "base/i18n/rtl.h"
+#if !defined(OS_IOS)
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#endif
+#endif
+
 #if defined(OS_ANDROID)
 #include "base/test/test_support_android.h"
 #endif
@@ -321,6 +329,22 @@
   }
 
   base::i18n::InitializeICU();
+  // On the Mac OS X command line, the default locale is *_POSIX. In Chromium,
+  // the locale is set via an OS X locale API and is never *_POSIX.
+  // Some tests (such as those involving word break iterator) will behave
+  // differently and fail if we use *POSIX locale. Setting it to en_US here
+  // does not affect tests that explicitly overrides the locale for testing.
+  // This can be an issue on all platforms other than Windows.
+  // TODO(jshin): Should we set the locale via an OS X locale API here?
+#if !defined(OS_WIN)
+#if defined(OS_IOS)
+  base::i18n::SetICUDefaultLocale("en_US");
+#else
+  std::string default_locale(uloc_getDefault());
+  if (EndsWith(default_locale, "POSIX", false))
+    base::i18n::SetICUDefaultLocale("en_US");
+#endif
+#endif
 
   CatchMaybeTests();
   ResetCommandLine();
diff --git a/base/time/time.h b/base/time/time.h
index 18de085..6d61861 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -178,6 +178,14 @@
     return delta_ / a.delta_;
   }
 
+  // Multiplicative computations with floats.
+  TimeDelta multiply_by(double a) const {
+    return TimeDelta(delta_ * a);
+  }
+  TimeDelta divide_by(double a) const {
+    return TimeDelta(delta_ / a);
+  }
+
   // Defined below because it depends on the definition of the other classes.
   Time operator+(Time t) const;
   TimeTicks operator+(TimeTicks t) const;
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index fdac59d..6387ec7 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -867,6 +867,15 @@
             TimeDelta::FromMicroseconds(min_int64_plus_two).magnitude());
 }
 
+
+TEST(TimeDelta, multiply_by) {
+  double d = 0.5;
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000).multiply_by(d));
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000).divide_by(d));
+}
+
 TEST(TimeDeltaLogging, DCheckEqCompiles) {
   DCHECK_EQ(TimeDelta(), TimeDelta());
 }
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index bf631b3..c8be8f8 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -9,22 +9,15 @@
 #include "base/compiler_specific.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/process_memory_dump.h"
-
-// TODO(primiano): in a separate CL rename DeleteTraceLogForTesting into
-// something like base::internal::TeardownSingletonForTesting so we don't have
-// to add a new friend to singleton each time.
-class DeleteTraceLogForTesting {
- public:
-  static void Delete() {
-    Singleton<
-        base::trace_event::MemoryDumpManager,
-        LeakySingletonTraits<base::trace_event::MemoryDumpManager>>::OnExit(0);
-  }
-};
+#include "base/trace_event/trace_event_argument.h"
 
 namespace base {
 namespace trace_event {
 
+namespace {
+MemoryDumpManager* g_instance_for_testing = nullptr;
+}
+
 // TODO(primiano): this should be smarter and should do something similar to
 // trace event synthetic delays.
 const char MemoryDumpManager::kTraceCategory[] =
@@ -32,13 +25,16 @@
 
 // static
 MemoryDumpManager* MemoryDumpManager::GetInstance() {
+  if (g_instance_for_testing)
+    return g_instance_for_testing;
+
   return Singleton<MemoryDumpManager,
                    LeakySingletonTraits<MemoryDumpManager>>::get();
 }
 
 // static
-void MemoryDumpManager::DeleteForTesting() {
-  DeleteTraceLogForTesting::Delete();
+void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
+  g_instance_for_testing = instance;
 }
 
 MemoryDumpManager::MemoryDumpManager() : memory_tracing_enabled_(0) {
@@ -96,15 +92,14 @@
 // Creates a dump point for the current process and appends it to the trace.
 void MemoryDumpManager::CreateLocalDumpPoint() {
   AutoLock lock(lock_);
-  // TRACE_EVENT_* macros don't induce scoped_refptr type inference, hence we
-  // need the base ConvertableToTraceFormat and the upcast below. The
-  // alternative would be unnecessarily expensive (double Acquire/Release).
-  scoped_refptr<ConvertableToTraceFormat> pmd(new ProcessMemoryDump());
+  scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
 
   for (MemoryDumpProvider* dump_provider : dump_providers_enabled_) {
-    dump_provider->DumpInto(static_cast<ProcessMemoryDump*>(pmd.get()));
+    dump_provider->DumpInto(pmd.get());
   }
 
+  scoped_refptr<TracedValue> value(new TracedValue());
+  pmd->AsValueInto(value.get());
   // TODO(primiano): add the dump point to the trace at this point.
 }
 
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index fbc71d5..1a22e61 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -50,17 +50,17 @@
   void OnTraceLogDisabled() override;
 
  private:
+  friend struct DefaultDeleter<MemoryDumpManager>;  // For the testing instance.
   friend struct DefaultSingletonTraits<MemoryDumpManager>;
   friend class MemoryDumpManagerTest;
 
   static const char kTraceCategory[];
 
+  static void SetInstanceForTesting(MemoryDumpManager* instance);
+
   MemoryDumpManager();
   virtual ~MemoryDumpManager();
 
-  // Tears down the singleton instance.
-  static void DeleteForTesting();
-
   // Broadcasts the dump requests to the other processes.
   void BroadcastDumpRequest();
 
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index b5337e9..1ba73e6 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -17,14 +17,16 @@
 class MemoryDumpManagerTest : public testing::Test {
  public:
   void SetUp() override {
+    mdm_.reset(new MemoryDumpManager());
+    MemoryDumpManager::SetInstanceForTesting(mdm_.get());
+    ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance());
     MemoryDumpManager::GetInstance()->Initialize();
-    mdm_ = MemoryDumpManager::GetInstance();
   }
 
   void TearDown() override {
-    MemoryDumpManager::DeleteForTesting();
+    MemoryDumpManager::SetInstanceForTesting(nullptr);
+    mdm_.reset();
     TraceLog::DeleteForTesting();
-    mdm_ = NULL;
   }
 
  protected:
@@ -37,7 +39,7 @@
 
   void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
 
-  MemoryDumpManager* mdm_;
+  scoped_ptr<MemoryDumpManager> mdm_;
 
  private:
   // We want our singleton torn down after each test.
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 0a3e096..6da9132 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -4,25 +4,25 @@
 
 #include "base/trace_event/process_memory_dump.h"
 
-#include "base/json/json_writer.h"
-#include "base/values.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "base/trace_event/trace_event_argument.h"
 
 namespace base {
 namespace trace_event {
 
-ProcessMemoryDump::ProcessMemoryDump() {
+ProcessMemoryDump::ProcessMemoryDump() : has_process_totals_(false) {
 }
 
 ProcessMemoryDump::~ProcessMemoryDump() {
 }
 
-void ProcessMemoryDump::AppendAsTraceFormat(std::string* out) const {
-  // Build up the [dumper name] -> [serialized snapshot] JSON dictionary.
-  DictionaryValue dict;
-  std::string json_dict;
-  // TODO(primiano): this will append here the actual dumps from the dumpers.
-  base::JSONWriter::Write(&dict, &json_dict);
-  *out += json_dict;
+void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
+  // Build up the [dumper name] -> [value] dictionary.
+  if (has_process_totals_) {
+    value->BeginDictionary("process_totals");
+    process_totals_.AsValueInto(value);
+    value->EndDictionary();
+  }
 }
 
 }  // namespace trace_event
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index ae42987..f70537b 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -6,27 +6,34 @@
 #define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
 
 #include "base/base_export.h"
-#include "base/basictypes.h"
-#include "base/trace_event/trace_event_impl.h"
+#include "base/trace_event/process_memory_totals.h"
 
 namespace base {
 namespace trace_event {
 
-// A container which holds the dumps produced by the MemoryDumpProvider(s)
-// for a specific process. ProcessMemoryDump is as a strongly typed container
-// which enforces the data model for each memory dump point.
-// At trace generation time (i.e. when AppendAsTraceFormat is called) the
-// ProcessMemoryDump will compose a key-value dictionary of the various dumps
-// obtained during at trace dump point time.
-class BASE_EXPORT ProcessMemoryDump : public ConvertableToTraceFormat {
+class ConvertableToTraceFormat;
+
+// ProcessMemoryDump is as a strongly typed container which enforces the data
+// model for each memory dump point and holds the dumps produced by the
+// MemoryDumpProvider(s) for a specific process.
+// At trace generation time (i.e. when AsValue() is called), ProcessMemoryDump
+// will compose a key-value dictionary of the various dumps obtained at trace
+// dump point time.
+class BASE_EXPORT ProcessMemoryDump {
  public:
   ProcessMemoryDump();
+  ~ProcessMemoryDump();
 
-  // ConvertableToTraceFormat implementation.
-  void AppendAsTraceFormat(std::string* out) const override;
+  // Called at trace generation time to populate the TracedValue.
+  void AsValueInto(TracedValue* value) const;
+
+  ProcessMemoryTotals* process_totals() { return &process_totals_; }
+  bool has_process_totals() const { return has_process_totals_; }
+  void set_has_process_totals() { has_process_totals_ = true; }
 
  private:
-  ~ProcessMemoryDump() override;
+  ProcessMemoryTotals process_totals_;
+  bool has_process_totals_;
 
   DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
 };
diff --git a/base/trace_event/process_memory_totals.cc b/base/trace_event/process_memory_totals.cc
new file mode 100644
index 0000000..41ad788
--- /dev/null
+++ b/base/trace_event/process_memory_totals.cc
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals.h"
+
+#include "base/trace_event/trace_event_argument.h"
+
+namespace base {
+namespace trace_event {
+
+void ProcessMemoryTotals::AsValueInto(TracedValue* value) const {
+  value->SetDouble("resident_set_bytes", resident_set_bytes_);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/process_memory_totals.h b/base/trace_event/process_memory_totals.h
new file mode 100644
index 0000000..1c99152
--- /dev/null
+++ b/base/trace_event/process_memory_totals.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace trace_event {
+
+class TracedValue;
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotals {
+ public:
+  ProcessMemoryTotals() {}
+
+  // Called at trace generation time to populate the TracedValue.
+  void AsValueInto(TracedValue* value) const;
+
+  uint64 resident_set_bytes() const { return resident_set_bytes_; }
+  void set_resident_set_bytes(uint64 value) { resident_set_bytes_ = value; }
+
+ private:
+  uint64 resident_set_bytes_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotals);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider.cc b/base/trace_event/process_memory_totals_dump_provider.cc
new file mode 100644
index 0000000..cda0ff1
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider.cc
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include "base/process/process_metrics.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+uint64 ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+namespace {
+ProcessMetrics* CreateProcessMetricsForCurrentProcess() {
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+  return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle());
+#else
+  return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle(), NULL);
+#endif
+}
+}  // namespace
+
+// static
+ProcessMemoryTotalsDumpProvider*
+ProcessMemoryTotalsDumpProvider::GetInstance() {
+  return Singleton<
+      ProcessMemoryTotalsDumpProvider,
+      LeakySingletonTraits<ProcessMemoryTotalsDumpProvider>>::get();
+}
+
+ProcessMemoryTotalsDumpProvider::ProcessMemoryTotalsDumpProvider()
+    : process_metrics_(CreateProcessMetricsForCurrentProcess()) {
+}
+
+ProcessMemoryTotalsDumpProvider::~ProcessMemoryTotalsDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot the memory counters for
+// the current process.
+void ProcessMemoryTotalsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+  const uint64 rss_bytes = rss_bytes_for_testing
+                               ? rss_bytes_for_testing
+                               : process_metrics_->GetWorkingSetSize();
+  pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+  pmd->set_has_process_totals();
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/process_memory_totals_dump_provider.h b/base/trace_event/process_memory_totals_dump_provider.h
new file mode 100644
index 0000000..45917a8
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+
+class ProcessMetrics;
+
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotalsDumpProvider : public MemoryDumpProvider {
+ public:
+  static ProcessMemoryTotalsDumpProvider* GetInstance();
+
+  // MemoryDumpProvider implementation.
+  void DumpInto(ProcessMemoryDump* pmd) override;
+
+ private:
+  friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>;
+  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryTotalsDumpProviderTest, DumpRSS);
+
+  static uint64 rss_bytes_for_testing;
+
+  ProcessMemoryTotalsDumpProvider();
+  ~ProcessMemoryTotalsDumpProvider() override;
+
+  scoped_ptr<ProcessMetrics> process_metrics_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotalsDumpProvider);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
new file mode 100644
index 0000000..4a60036
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
+  auto mdptp = ProcessMemoryTotalsDumpProvider::GetInstance();
+  scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump());
+  scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump());
+
+  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024;
+  mdptp->DumpInto(pmd_before.get());
+
+  // Pretend that the RSS of the process increased of +1M.
+  const size_t kAllocSize = 1048576;
+  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing += kAllocSize;
+
+  mdptp->DumpInto(pmd_after.get());
+
+  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+  ASSERT_TRUE(pmd_before->has_process_totals());
+  ASSERT_TRUE(pmd_after->has_process_totals());
+
+  const uint64 rss_before = pmd_before->process_totals()->resident_set_bytes();
+  const uint64 rss_after = pmd_after->process_totals()->resident_set_bytes();
+
+  EXPECT_NE(0U, rss_before);
+  EXPECT_NE(0U, rss_after);
+
+  EXPECT_EQ(rss_after - rss_before, kAllocSize);
+}
+
+}  // namespace trace_Event
+}  // namespace base
diff --git a/base/trace_event/trace_event_synthetic_delay.cc b/base/trace_event/trace_event_synthetic_delay.cc
index 4b957c3..bad79cc 100644
--- a/base/trace_event/trace_event_synthetic_delay.cc
+++ b/base/trace_event/trace_event_synthetic_delay.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/memory/singleton.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/trace_event/trace_event_synthetic_delay.h"
 
 namespace {
diff --git a/base/version.cc b/base/version.cc
index 6318b35..933356e 100644
--- a/base/version.cc
+++ b/base/version.cc
@@ -31,6 +31,8 @@
 
   for (std::vector<std::string>::const_iterator it = numbers.begin();
        it != numbers.end(); ++it) {
+    if (StartsWithASCII(*it, "+", false))
+      return false;
     int num;
     if (!StringToInt(*it, &num))
       return false;
@@ -42,8 +44,8 @@
     if (num > max)
       return false;
 
-    // This throws out things like +3, or 032.
-    if (IntToString(num) != *it)
+    // This throws out leading zeros for the first item only.
+    if (it == numbers.begin() && IntToString(num) != *it)
       return false;
 
     parsed->push_back(static_cast<uint16>(num));
diff --git a/base/version_unittest.cc b/base/version_unittest.cc
index 3119c39..46d8255 100644
--- a/base/version_unittest.cc
+++ b/base/version_unittest.cc
@@ -41,16 +41,22 @@
     {".", 0, false},
     {" . ", 0, false},
     {"0", 1, true},
+    {"0.", 0, false},
     {"0.0", 2, true},
     {"65537.0", 0, false},
     {"-1.0", 0, false},
     {"1.-1.0", 0, false},
+    {"1,--1.0", 0, false},
     {"+1.0", 0, false},
     {"1.+1.0", 0, false},
+    {"1+1.0", 0, false},
+    {"++1.0", 0, false},
     {"1.0a", 0, false},
     {"1.2.3.4.5.6.7.8.9.0", 10, true},
     {"02.1", 0, false},
+    {"0.01", 2, true},
     {"f.1", 0, false},
+    {"15.007.20011", 3, true},
   };
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
@@ -77,6 +83,7 @@
     {"1.1", "1.0.1", 1},
     {"1.0.0", "1.0", 0},
     {"1.0.3", "1.0.20", -1},
+    {"11.0.10", "15.007.20011", -1},
   };
   for (size_t i = 0; i < arraysize(cases); ++i) {
     Version lhs(cases[i].lhs);
diff --git a/base/win/pe_image.cc b/base/win/pe_image.cc
index 572b4d9..e226b6a 100644
--- a/base/win/pe_image.cc
+++ b/base/win/pe_image.cc
@@ -20,6 +20,9 @@
 
 namespace {
 
+  // PdbInfo Signature
+  const DWORD kPdbInfoSignature = 'SDSR';
+
   // Compare two strings byte by byte on an unsigned basis.
   //   if s1 == s2, return 0
   //   if s1 < s2, return negative
@@ -35,6 +38,12 @@
             *reinterpret_cast<const unsigned char*>(s2));
   }
 
+  struct PdbInfo {
+    DWORD Signature;
+    GUID Guid;
+    DWORD Age;
+    char PdbFileName[1];
+  };
 }  // namespace
 
 // Callback used to enumerate imports. See EnumImportChunksFunction.
@@ -142,6 +151,36 @@
   return ret;
 }
 
+bool PEImage::GetDebugId(LPGUID guid, LPDWORD age) const {
+  if (NULL == guid || NULL == age) {
+    return false;
+  }
+
+  DWORD debug_directory_size =
+      GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG);
+  PIMAGE_DEBUG_DIRECTORY debug_directory =
+      reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(
+      GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG));
+
+  size_t directory_count =
+      debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY);
+
+  for (size_t index = 0; index < directory_count; ++index) {
+    if (debug_directory[index].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
+      PdbInfo* pdb_info = reinterpret_cast<PdbInfo*>(
+          RVAToAddr(debug_directory[index].AddressOfRawData));
+      if (pdb_info->Signature != kPdbInfoSignature) {
+        // Unsupported PdbInfo signature
+        return false;
+      }
+      *guid = pdb_info->Guid;
+      *age = pdb_info->Age;
+      return true;
+    }
+  }
+  return false;
+}
+
 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
 
diff --git a/base/win/pe_image.h b/base/win/pe_image.h
index dde1b48..5cef537 100644
--- a/base/win/pe_image.h
+++ b/base/win/pe_image.h
@@ -132,6 +132,9 @@
   // Returns the exports directory.
   PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
 
+  // Returns the debug id (guid+age).
+  bool GetDebugId(LPGUID guid, LPDWORD age) const;
+
   // Returns a given export entry.
   // Use: e = image.GetExportEntry(f);
   // Pre: 'f' is either a zero terminated string or ordinal
diff --git a/base/win/pe_image_unittest.cc b/base/win/pe_image_unittest.cc
index 238c924..af4209b 100644
--- a/base/win/pe_image_unittest.cc
+++ b/base/win/pe_image_unittest.cc
@@ -267,5 +267,21 @@
   FreeLibrary(module);
 }
 
+// Test that we can get debug id out of a module.
+TEST(PEImageTest, GetDebugId) {
+  HMODULE module = LoadLibrary(L"advapi32.dll");
+  ASSERT_TRUE(NULL != module);
+
+  PEImage pe(module);
+  GUID guid = {0};
+  DWORD age = 0;
+  EXPECT_TRUE(pe.GetDebugId(&guid, &age));
+
+  GUID empty_guid = {0};
+  EXPECT_TRUE(!IsEqualGUID(empty_guid, guid));
+  EXPECT_NE(0U, age);
+  FreeLibrary(module);
+}
+
 }  // namespace win
 }  // namespace base
diff --git a/build/all.gyp b/build/all.gyp
index 93cac8c..8ad63b3 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -519,7 +519,6 @@
           'target_name': 'chromium_builder_nacl_win_integration',
           'type': 'none',
           'dependencies': [
-            'chromium_builder_qa', # needed for pyauto
             'chromium_builder_tests',
           ],
         }, # target_name: chromium_builder_nacl_win_integration
@@ -636,42 +635,10 @@
           ],
         }, # target_name: chromium_gpu_debug_builder
         {
-          'target_name': 'chromium_builder_qa',
-          'type': 'none',
-          'dependencies': [
-            '../chrome/chrome.gyp:chrome',
-            # Dependencies of pyauto_functional tests.
-            '../remoting/remoting.gyp:remoting_webapp',
-          ],
-          'conditions': [
-            ['OS=="mac"', {
-              'dependencies': [
-                '../remoting/remoting.gyp:remoting_me2me_host_archive',
-              ],
-            }],
-            ['OS=="win"', {
-              'dependencies': [
-                '../chrome/chrome.gyp:crash_service',
-              ],
-            }],
-            ['OS=="win" and target_arch=="ia32"', {
-              'dependencies': [
-                '../chrome/chrome.gyp:crash_service_win64',
-              ],
-            }],
-            ['OS=="win" and component != "shared_library" and wix_exists == "True" and sas_dll_exists == "True"', {
-              'dependencies': [
-                '../remoting/remoting.gyp:remoting_host_installation',
-              ],
-            }],
-          ],
-        }, # target_name: chromium_builder_qa
-        {
           'target_name': 'chromium_builder_perf_av',
           'type': 'none',
           'dependencies': [
             'blink_tests', # to run layout tests
-            'chromium_builder_qa',  # needed for perf pyauto tests
           ],
         },  # target_name: chromium_builder_perf_av
         {
@@ -1093,10 +1060,6 @@
           ],
         },
         {
-          'target_name': 'chromium_builder_win_cf',
-          'type': 'none',
-        },
-        {
           'target_name': 'chromium_builder_dbg_tsan_win',
           'type': 'none',
           'dependencies': [
diff --git a/build/android/android_exports.gyp b/build/android/android_exports.gyp
index c259eee..bf3424d 100644
--- a/build/android/android_exports.gyp
+++ b/build/android/android_exports.gyp
@@ -2,13 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# This target is only used when android_webview_build==1 - it implements a
+# whitelist for exported symbols to minimise the binary size and prevent us
+# accidentally exposing things we don't mean to expose.
+
 {
+  'variables': {
+    'android_linker_script%': '<(SHARED_INTERMEDIATE_DIR)/android_webview_export_whitelist.lst',
+  },
   'targets': [
     {
       'target_name': 'android_exports',
       'type': 'none',
       'inputs': [
-        '<(DEPTH)/build/android/android_exports.lst',
+        '<(DEPTH)/build/android/android_webview_export_whitelist.lst',
       ],
       'outputs': [
         '<(android_linker_script)',
@@ -28,9 +35,6 @@
               # Only export symbols that are specified in version script.
               '-Wl,--version-script=<(android_linker_script)',
             ],
-            'ldflags!': [
-              '-Wl,--exclude-libs=ALL',
-            ],
           },
         }],
       ],
diff --git a/build/android/android_no_jni_exports.lst b/build/android/android_no_jni_exports.lst
new file mode 100644
index 0000000..ffc6cf7
--- /dev/null
+++ b/build/android/android_no_jni_exports.lst
@@ -0,0 +1,17 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script makes all JNI exported symbols local, to prevent the JVM from
+# being able to find them, enforcing use of manual JNI function registration.
+# This is used for all Android binaries by default, unless they explicitly state
+# that they want JNI exported symbols to remain visible, as we need to ensure
+# the manual registration path is correct to maintain compatibility with the
+# crazy linker.
+# Check ld version script manual:
+# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION
+
+{
+  local:
+    Java_*;
+};
diff --git a/build/android/android_exports.lst b/build/android/android_webview_export_whitelist.lst
similarity index 72%
rename from build/android/android_exports.lst
rename to build/android/android_webview_export_whitelist.lst
index 6eee232..2a56a75 100644
--- a/build/android/android_exports.lst
+++ b/build/android/android_webview_export_whitelist.lst
@@ -2,7 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Default exports specification for chromium shared libraries on android.
+# Exports specification for android_webview_build==1, which uses a whitelist to
+# enforce only specific symbols being exported.
 # Check ld version script manual:
 # https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION
 
diff --git a/build/android/buildbot/bb_device_status_check.py b/build/android/buildbot/bb_device_status_check.py
index 6de2723..3bec158 100755
--- a/build/android/buildbot/bb_device_status_check.py
+++ b/build/android/buildbot/bb_device_status_check.py
@@ -337,11 +337,12 @@
         zip(*[DeviceInfo(dev, options) for dev in devices]))
 
   # Write device info to file for buildbot info display.
-  with open('/home/chrome-bot/.adb_device_info', 'w') as f:
-    for device in json_data:
-      f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'],
-          device['build'], float(device['battery']['temperature']) / 10,
-          device['battery']['level']))
+  if os.path.exists('/home/chrome-bot'):
+    with open('/home/chrome-bot/.adb_device_info', 'w') as f:
+      for device in json_data:
+        f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'],
+            device['build'], float(device['battery']['temperature']) / 10,
+            device['battery']['level']))
 
   err_msg = CheckForMissingDevices(options, devices) or []
 
diff --git a/build/android/findbugs_filter/findbugs_known_bugs.txt b/build/android/findbugs_filter/findbugs_known_bugs.txt
index 09c12b2..9afba51 100644
--- a/build/android/findbugs_filter/findbugs_known_bugs.txt
+++ b/build/android/findbugs_filter/findbugs_known_bugs.txt
@@ -5,3 +5,4 @@
 M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.webContents  In PendingDocumentData.java
 M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.originalIntent  In PendingDocumentData.java
 M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.url  In PendingDocumentData.java
+M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.requestId  In PendingDocumentData.java
diff --git a/build/android/provision_devices.py b/build/android/provision_devices.py
index 65054fe..824e642 100755
--- a/build/android/provision_devices.py
+++ b/build/android/provision_devices.py
@@ -178,7 +178,7 @@
     if options.disable_network:
       device_settings.ConfigureContentSettings(
           device, device_settings.NETWORK_DISABLED_SETTINGS)
-    if options.wait_for_battery:
+    if options.min_battery_level is not None:
       try:
         battery_info = device.old_interface.GetBatteryInfo()
       except Exception as e:
@@ -241,24 +241,11 @@
   logging.getLogger().addHandler(custom_handler)
   logging.getLogger().setLevel(logging.INFO)
 
-  # TODO(perezju): This script used to rely on the builder name to determine
-  # the desired device configuration for perf bots. To safely phase this out,
-  # we now:
-  # - expose these configuration settings as command line options
-  # - set default values for these options based on the builder name, thus
-  #   matching the previous behaviour of the script on all bots.
-  # - explicitly adding these options on the perf bots will also maintain the
-  #   script behaviour, namely:
-  #     --wait-for-battery --disable-network --disable-java-debug
-  # - after all perf-bot recipes are updated, we can remove the following
-  #   builder-name-sniffing code and replace |is_perf| with |False|.
-  is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower()
-
   # Recommended options on perf bots:
   # --disable-network
   #     TODO(tonyg): We eventually want network on. However, currently radios
   #     can cause perfbots to drain faster than they charge.
-  # --wait-for-battery
+  # --min-battery-level 95
   #     Some perf bots run benchmarks with USB charging disabled which leads
   #     to gradual draining of the battery. We must wait for a full charge
   #     before starting a run in order to keep the devices online.
@@ -274,20 +261,15 @@
                       help='when wiping the device, max number of seconds to'
                       ' wait after each reboot '
                       '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
-  parser.add_argument('--wait-for-battery', action='store_true',
-                      default=is_perf,
-                      help='wait for the battery on the devices to charge')
-  parser.add_argument('--min-battery-level', default=95, type=int,
-                      metavar='NUM',
-                      help='when waiting for battery, minimum battery level'
-                      ' required to continue (default: %(default)s)')
+  parser.add_argument('--min-battery-level', type=int, metavar='NUM',
+                      help='wait for the device to reach this minimum battery'
+                      ' level before trying to continue')
   parser.add_argument('--disable-location', action='store_true',
                       help='disable Google location services on devices')
   parser.add_argument('--disable-network', action='store_true',
-                      default=is_perf,
                       help='disable network access on devices')
   parser.add_argument('--disable-java-debug', action='store_false',
-                      dest='enable_java_debug', default=not is_perf,
+                      dest='enable_java_debug', default=True,
                       help='disable Java property asserts and JNI checking')
   parser.add_argument('-t', '--target', default='Debug',
                       help='the build target (default: %(default)s)')
diff --git a/build/android/pylib/base/base_test_result.py b/build/android/pylib/base/base_test_result.py
index 508b988..58200f6 100644
--- a/build/android/pylib/base/base_test_result.py
+++ b/build/android/pylib/base/base_test_result.py
@@ -77,6 +77,10 @@
     """Get the test duration."""
     return self._duration
 
+  def SetLog(self, log):
+    """Set the test log."""
+    self._log = log
+
   def GetLog(self):
     """Get the test log."""
     return self._log
diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py
index f29f5c7..c954508 100644
--- a/build/android/pylib/device/adb_wrapper.py
+++ b/build/android/pylib/device/adb_wrapper.py
@@ -299,14 +299,14 @@
           cmd, 'path does not specify an accessible directory in the device',
           device_serial=self._device_serial)
 
-  def Logcat(self, clear=False, dump=False, filter_spec=None,
+  def Logcat(self, clear=False, dump=False, filter_specs=None,
              logcat_format=None, timeout=None, retries=_DEFAULT_RETRIES):
     """Get an iterable over the logcat output.
 
     Args:
       clear: If true, clear the logcat.
       dump: If true, dump the current logcat contents.
-      filter_spec: If set, spec to filter the logcat.
+      filter_specs: If set, a list of specs to filter the logcat.
       logcat_format: If set, the format in which the logcat should be output.
         Options include "brief", "process", "tag", "thread", "raw", "time",
         "threadtime", and "long"
@@ -328,14 +328,14 @@
       use_iter = False
     if logcat_format:
       cmd.extend(['-v', logcat_format])
-    if filter_spec is not None:
-      cmd.append(filter_spec)
+    if filter_specs:
+      cmd.extend(filter_specs)
 
     if use_iter:
       return self._IterRunDeviceAdbCmd(cmd, timeout)
     else:
       timeout = timeout if timeout is not None else _DEFAULT_TIMEOUT
-      return self._RunDeviceAdbCmd(cmd, timeout, retries)
+      return self._RunDeviceAdbCmd(cmd, timeout, retries).splitlines()
 
   def Forward(self, local, remote, timeout=_DEFAULT_TIMEOUT,
               retries=_DEFAULT_RETRIES):
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index eba5e02..fb882e7 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -8,6 +8,8 @@
 """
 # pylint: disable=unused-argument
 
+import collections
+import itertools
 import logging
 import multiprocessing
 import os
@@ -1306,14 +1308,54 @@
       retries: number of retries
 
     Returns:
-      A 2-tuple containing:
-        - A dict containing the overall memory usage statistics for the PID.
-        - A dict containing memory usage statistics broken down by mapping.
+      A dict containing memory usage statistics for the PID. May include:
+        Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
+        Private_Dirty, VmHWM
 
     Raises:
       CommandTimeoutError on timeout.
     """
-    return self.old_interface.GetMemoryUsageForPid(pid)
+    result = collections.defaultdict(int)
+
+    try:
+      result.update(self._GetMemoryUsageForPidFromSmaps(pid))
+    except device_errors.CommandFailedError:
+      logging.exception('Error getting memory usage from smaps')
+
+    try:
+      result.update(self._GetMemoryUsageForPidFromStatus(pid))
+    except device_errors.CommandFailedError:
+      logging.exception('Error getting memory usage from status')
+
+    return result
+
+  def _GetMemoryUsageForPidFromSmaps(self, pid):
+    SMAPS_COLUMNS = (
+        'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
+        'Private_Dirty')
+
+    showmap_out = self.RunShellCommand(
+        ['showmap', str(pid)], as_root=True, check_return=True)
+    if not showmap_out:
+      raise device_errors.CommandFailedError('No output from showmap')
+
+    split_totals = showmap_out[-1].split()
+    if (not split_totals
+        or len(split_totals) != 9
+        or split_totals[-1] != 'TOTAL'):
+      raise device_errors.CommandFailedError(
+          'Invalid output from showmap: %s' % '\n'.join(showmap_out))
+
+    return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
+
+  def _GetMemoryUsageForPidFromStatus(self, pid):
+    for line in self.ReadFile(
+        '/proc/%s/status' % str(pid), as_root=True).splitlines():
+      if line.startswith('VmHWM:'):
+        return {'VmHWM': int(line.split()[1])}
+    else:
+      raise device_errors.CommandFailedError(
+          'Could not find memory peak value for pid %s', str(pid))
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
@@ -1386,6 +1428,8 @@
     """
     if not devices:
       devices = adb_wrapper.AdbWrapper.GetDevices()
+      if not devices:
+        raise device_errors.NoDevicesError()
     devices = [d if isinstance(d, cls) else cls(d) for d in devices]
     if async:
       return parallelizer.Parallelizer(devices)
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index 8a25f25..3f4ef59 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -39,7 +39,7 @@
 import mock # pylint: disable=F0401
 
 
-class DeviceUtilsTest(unittest.TestCase):
+class DeviceUtilsInitTest(unittest.TestCase):
 
   def testInitWithStr(self):
     serial_as_str = str('0123456789abcdef')
@@ -121,79 +121,6 @@
     self.mocked = mocked
 
 
-class DeviceUtilsOldImplTest(unittest.TestCase):
-
-  class AndroidCommandsCalls(object):
-
-    def __init__(self, test_case, cmd_ret, comp):
-      self._cmds = cmd_ret
-      self._comp = comp
-      self._run_command = _PatchedFunction()
-      self._test_case = test_case
-      self._total_received = 0
-
-    def __enter__(self):
-      self._run_command.patched = mock.patch(
-          'run_command.RunCommand',
-          side_effect=lambda c, **kw: self._ret(c))
-      self._run_command.mocked = self._run_command.patched.__enter__()
-
-    def _ret(self, actual_cmd):
-      if sys.exc_info()[0] is None:
-        on_failure_fmt = ('\n'
-                          '  received command: %s\n'
-                          '  expected command: %s')
-        self._test_case.assertGreater(
-            len(self._cmds), self._total_received,
-            msg=on_failure_fmt % (actual_cmd, None))
-        expected_cmd, ret = self._cmds[self._total_received]
-        self._total_received += 1
-        self._test_case.assertTrue(
-            self._comp(expected_cmd, actual_cmd),
-            msg=on_failure_fmt % (actual_cmd, expected_cmd))
-        return ret
-      return ''
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-      self._run_command.patched.__exit__(exc_type, exc_val, exc_tb)
-      if exc_type is None:
-        on_failure = "adb commands don't match.\nExpected:%s\nActual:%s" % (
-            ''.join('\n  %s' % c for c, _ in self._cmds),
-            ''.join('\n  %s' % a[0]
-                    for _, a, kw in self._run_command.mocked.mock_calls))
-        self._test_case.assertEqual(
-          len(self._cmds), len(self._run_command.mocked.mock_calls),
-          msg=on_failure)
-        for (expected_cmd, _r), (_n, actual_args, actual_kwargs) in zip(
-            self._cmds, self._run_command.mocked.mock_calls):
-          self._test_case.assertEqual(1, len(actual_args), msg=on_failure)
-          self._test_case.assertTrue(self._comp(expected_cmd, actual_args[0]),
-                                     msg=on_failure)
-          self._test_case.assertTrue('timeout_time' in actual_kwargs,
-                                     msg=on_failure)
-          self._test_case.assertTrue('retry_count' in actual_kwargs,
-                                     msg=on_failure)
-
-  def assertNoAdbCalls(self):
-    return type(self).AndroidCommandsCalls(self, [], str.__eq__)
-
-  def assertCalls(self, cmd, ret, comp=str.__eq__):
-    return type(self).AndroidCommandsCalls(self, [(cmd, ret)], comp)
-
-  def assertCallsSequence(self, cmd_ret, comp=str.__eq__):
-    return type(self).AndroidCommandsCalls(self, cmd_ret, comp)
-
-  def setUp(self):
-    self._get_adb_path_patch = mock.patch('pylib.constants.GetAdbPath',
-                                          mock.Mock(return_value='adb'))
-    self._get_adb_path_patch.start()
-    self.device = device_utils.DeviceUtils(
-        '0123456789abcdef', default_timeout=1, default_retries=0)
-
-  def tearDown(self):
-    self._get_adb_path_patch.stop()
-
-
 def _AdbWrapperMock(test_serial):
   adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
   adb.__str__ = mock.Mock(return_value=test_serial)
@@ -201,7 +128,7 @@
   return adb
 
 
-class DeviceUtilsNewImplTest(mock_calls.TestCase):
+class DeviceUtilsTest(mock_calls.TestCase):
 
   def setUp(self):
     self.adb = _AdbWrapperMock('0123456789abcdef')
@@ -230,7 +157,7 @@
         msg, str(self.device)))
 
 
-class DeviceUtilsIsOnlineTest(DeviceUtilsNewImplTest):
+class DeviceUtilsIsOnlineTest(DeviceUtilsTest):
 
   def testIsOnline_true(self):
     with self.assertCall(self.call.adb.GetState(), 'device'):
@@ -245,7 +172,7 @@
       self.assertFalse(self.device.IsOnline())
 
 
-class DeviceUtilsHasRootTest(DeviceUtilsNewImplTest):
+class DeviceUtilsHasRootTest(DeviceUtilsTest):
 
   def testHasRoot_true(self):
     with self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n'):
@@ -256,7 +183,7 @@
       self.assertFalse(self.device.HasRoot())
 
 
-class DeviceUtilsEnableRootTest(DeviceUtilsNewImplTest):
+class DeviceUtilsEnableRootTest(DeviceUtilsTest):
 
   def testEnableRoot_succeeds(self):
     with self.assertCalls(
@@ -279,7 +206,7 @@
         self.device.EnableRoot()
 
 
-class DeviceUtilsIsUserBuildTest(DeviceUtilsNewImplTest):
+class DeviceUtilsIsUserBuildTest(DeviceUtilsTest):
 
   def testIsUserBuild_yes(self):
     with self.assertCall(
@@ -292,7 +219,7 @@
       self.assertFalse(self.device.IsUserBuild())
 
 
-class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsTest):
 
   def testGetExternalStoragePath_succeeds(self):
     with self.assertCall(
@@ -306,7 +233,7 @@
         self.device.GetExternalStoragePath()
 
 
-class DeviceUtilsGetApplicationPathTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetApplicationPathTest(DeviceUtilsTest):
 
   def testGetApplicationPath_exists(self):
     with self.assertCalls(
@@ -333,7 +260,7 @@
 
 
 @mock.patch('time.sleep', mock.Mock())
-class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsNewImplTest):
+class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
 
   def testWaitUntilFullyBooted_succeedsNoWifi(self):
     with self.assertCalls(
@@ -443,7 +370,7 @@
 
 
 @mock.patch('time.sleep', mock.Mock())
-class DeviceUtilsRebootTest(DeviceUtilsNewImplTest):
+class DeviceUtilsRebootTest(DeviceUtilsTest):
 
   def testReboot_nonBlocking(self):
     with self.assertCalls(
@@ -469,7 +396,7 @@
       self.device.Reboot(block=True, wifi=True)
 
 
-class DeviceUtilsInstallTest(DeviceUtilsNewImplTest):
+class DeviceUtilsInstallTest(DeviceUtilsTest):
 
   def testInstall_noPriorInstall(self):
     with self.assertCalls(
@@ -526,7 +453,7 @@
         self.device.Install('/fake/test/app.apk', retries=0)
 
 
-class DeviceUtilsRunShellCommandTest(DeviceUtilsNewImplTest):
+class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
 
   def setUp(self):
     super(DeviceUtilsRunShellCommandTest, self).setUp()
@@ -656,7 +583,7 @@
                         self.device.RunShellCommand(cmd, check_return=False))
 
 
-class DeviceUtilsGetDevicePieWrapper(DeviceUtilsNewImplTest):
+class DeviceUtilsGetDevicePieWrapper(DeviceUtilsTest):
 
   def testGetDevicePieWrapper_jb(self):
     with self.assertCall(
@@ -675,7 +602,7 @@
 
 
 @mock.patch('time.sleep', mock.Mock())
-class DeviceUtilsKillAllTest(DeviceUtilsNewImplTest):
+class DeviceUtilsKillAllTest(DeviceUtilsTest):
 
   def testKillAll_noMatchingProcesses(self):
     with self.assertCall(self.call.adb.Shell('ps'),
@@ -726,7 +653,7 @@
           self.device.KillAll('some.process', signum=signal.SIGTERM))
 
 
-class DeviceUtilsStartActivityTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStartActivityTest(DeviceUtilsTest):
 
   def testStartActivity_actionOnly(self):
     test_intent = intent.Intent(action='android.intent.action.VIEW')
@@ -890,7 +817,7 @@
       self.device.StartActivity(test_intent)
 
 
-class DeviceUtilsStartInstrumentationTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStartInstrumentationTest(DeviceUtilsTest):
 
   def testStartInstrumentation_nothing(self):
     with self.assertCalls(
@@ -932,7 +859,7 @@
           finish=False, raw=False, extras={'foo': 'Foo', 'bar': 'Bar'})
 
 
-class DeviceUtilsBroadcastIntentTest(DeviceUtilsNewImplTest):
+class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest):
 
   def testBroadcastIntent_noExtras(self):
     test_intent = intent.Intent(action='test.package.with.an.INTENT')
@@ -960,7 +887,7 @@
       self.device.BroadcastIntent(test_intent)
 
 
-class DeviceUtilsGoHomeTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGoHomeTest(DeviceUtilsTest):
 
   def testGoHome(self):
     with self.assertCall(
@@ -970,7 +897,7 @@
       self.device.GoHome()
 
 
-class DeviceUtilsForceStopTest(DeviceUtilsNewImplTest):
+class DeviceUtilsForceStopTest(DeviceUtilsTest):
 
   def testForceStop(self):
     with self.assertCall(
@@ -979,7 +906,7 @@
       self.device.ForceStop('this.is.a.test.package')
 
 
-class DeviceUtilsClearApplicationStateTest(DeviceUtilsNewImplTest):
+class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
 
   def testClearApplicationState_packageDoesntExist(self):
     with self.assertCalls(
@@ -1012,14 +939,14 @@
       self.device.ClearApplicationState('this.package.exists')
 
 
-class DeviceUtilsSendKeyEventTest(DeviceUtilsNewImplTest):
+class DeviceUtilsSendKeyEventTest(DeviceUtilsTest):
 
   def testSendKeyEvent(self):
     with self.assertCall(self.call.adb.Shell('input keyevent 66'), ''):
       self.device.SendKeyEvent(66)
 
 
-class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsNewImplTest):
+class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsTest):
 
   def testPushChangedFilesIndividually_empty(self):
     test_files = []
@@ -1041,7 +968,7 @@
       self.device._PushChangedFilesIndividually(test_files)
 
 
-class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsNewImplTest):
+class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsTest):
 
   def testPushChangedFilesZipped_empty(self):
     test_files = []
@@ -1080,7 +1007,7 @@
          ('/test/host/path/file2', '/test/device/path/file2')])
 
 
-class DeviceUtilsFileExistsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsFileExistsTest(DeviceUtilsTest):
 
   def testFileExists_usingTest_fileExists(self):
     with self.assertCall(
@@ -1096,7 +1023,7 @@
       self.assertFalse(self.device.FileExists('/does/not/exist'))
 
 
-class DeviceUtilsPullFileTest(DeviceUtilsNewImplTest):
+class DeviceUtilsPullFileTest(DeviceUtilsTest):
 
   def testPullFile_existsOnDevice(self):
     with mock.patch('os.path.exists', return_value=True):
@@ -1117,7 +1044,7 @@
                                '/test/file/host/path')
 
 
-class DeviceUtilsReadFileTest(DeviceUtilsNewImplTest):
+class DeviceUtilsReadFileTest(DeviceUtilsTest):
 
   def testReadFile_exists(self):
     with self.assertCall(
@@ -1145,7 +1072,7 @@
                                as_root=True))
 
 
-class DeviceUtilsWriteFileTest(DeviceUtilsNewImplTest):
+class DeviceUtilsWriteFileTest(DeviceUtilsTest):
 
   def testWriteFileWithPush_success(self):
     tmp_host = MockTempFile('/tmp/file/on.host')
@@ -1208,7 +1135,7 @@
       self.device.WriteFile('/test/file', 'contents', as_root=True)
 
 
-class DeviceUtilsLsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsLsTest(DeviceUtilsTest):
 
   def testLs_directory(self):
     result = [('.', adb_wrapper.DeviceStat(16889, 4096, 1417436123)),
@@ -1226,7 +1153,7 @@
                         self.device.Ls('/data/local/tmp/testfile.txt'))
 
 
-class DeviceUtilsStatTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStatTest(DeviceUtilsTest):
 
   def testStat_file(self):
     result = [('.', adb_wrapper.DeviceStat(16889, 4096, 1417436123)),
@@ -1256,7 +1183,7 @@
         self.device.Stat('/data/local/tmp/does.not.exist.txt')
 
 
-class DeviceUtilsSetJavaAssertsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsSetJavaAssertsTest(DeviceUtilsTest):
 
   def testSetJavaAsserts_enable(self):
     with self.assertCalls(
@@ -1296,7 +1223,7 @@
       self.assertFalse(self.device.SetJavaAsserts(True))
 
 
-class DeviceUtilsGetPropTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetPropTest(DeviceUtilsTest):
 
   def testGetProp_exists(self):
     with self.assertCall(
@@ -1330,7 +1257,7 @@
                                            cache=True, retries=3))
 
 
-class DeviceUtilsSetPropTest(DeviceUtilsNewImplTest):
+class DeviceUtilsSetPropTest(DeviceUtilsTest):
 
   def testSetProp(self):
     with self.assertCall(
@@ -1351,7 +1278,7 @@
         self.device.SetProp('test.property', 'new_value', check=True)
 
 
-class DeviceUtilsGetPidsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetPidsTest(DeviceUtilsTest):
 
   def testGetPids_noMatches(self):
     with self.assertCall(self.call.adb.Shell('ps'),
@@ -1387,7 +1314,7 @@
           self.device.GetPids('exact.match'))
 
 
-class DeviceUtilsTakeScreenshotTest(DeviceUtilsNewImplTest):
+class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest):
 
   def testTakeScreenshot_fileNameProvided(self):
     with self.assertCalls(
@@ -1401,22 +1328,18 @@
       self.device.TakeScreenshot('/test/host/screenshot.png')
 
 
-class DeviceUtilsGetMemoryUsageForPidTest(DeviceUtilsOldImplTest):
+class DeviceUtilsGetMemoryUsageForPidTest(DeviceUtilsTest):
 
   def setUp(self):
     super(DeviceUtilsGetMemoryUsageForPidTest, self).setUp()
-    self.device.old_interface._privileged_command_runner = (
-        self.device.old_interface.RunShellCommand)
-    self.device.old_interface._protected_file_access_method_initialized = True
 
   def testGetMemoryUsageForPid_validPid(self):
-    with self.assertCallsSequence([
-        ("adb -s 0123456789abcdef shell 'showmap 1234'",
-         '100 101 102 103 104 105 106 107 TOTAL\r\n'),
-        ("adb -s 0123456789abcdef shell "
-            "'cat \"/proc/1234/status\" 2> /dev/null'",
-         'VmHWM: 1024 kB')
-        ]):
+    with self.assertCalls(
+        (self.call.device.RunShellCommand(
+            ['showmap', '1234'], as_root=True, check_return=True),
+         ['100 101 102 103 104 105 106 107 TOTAL']),
+        (self.call.device.ReadFile('/proc/1234/status', as_root=True),
+         'VmHWM: 1024 kB\n')):
       self.assertEqual(
           {
             'Size': 100,
@@ -1430,14 +1353,36 @@
           },
           self.device.GetMemoryUsageForPid(1234))
 
-  def testGetMemoryUsageForPid_invalidPid(self):
+  def testGetMemoryUsageForPid_noSmaps(self):
     with self.assertCalls(
-        "adb -s 0123456789abcdef shell 'showmap 4321'",
-        'cannot open /proc/4321/smaps: No such file or directory\r\n'):
-      self.assertEqual({}, self.device.GetMemoryUsageForPid(4321))
+        (self.call.device.RunShellCommand(
+            ['showmap', '4321'], as_root=True, check_return=True),
+         ['cannot open /proc/4321/smaps: No such file or directory']),
+        (self.call.device.ReadFile('/proc/4321/status', as_root=True),
+         'VmHWM: 1024 kb\n')):
+      self.assertEquals({'VmHWM': 1024}, self.device.GetMemoryUsageForPid(4321))
+
+  def testGetMemoryUsageForPid_noStatus(self):
+    with self.assertCalls(
+        (self.call.device.RunShellCommand(
+            ['showmap', '4321'], as_root=True, check_return=True),
+         ['100 101 102 103 104 105 106 107 TOTAL']),
+        (self.call.device.ReadFile('/proc/4321/status', as_root=True),
+         self.CommandError())):
+      self.assertEquals(
+          {
+            'Size': 100,
+            'Rss': 101,
+            'Pss': 102,
+            'Shared_Clean': 103,
+            'Shared_Dirty': 104,
+            'Private_Clean': 105,
+            'Private_Dirty': 106,
+          },
+          self.device.GetMemoryUsageForPid(4321))
 
 
-class DeviceUtilsStrTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStrTest(DeviceUtilsTest):
 
   def testStr_returnsSerial(self):
     with self.assertCalls(
@@ -1459,6 +1404,12 @@
           and serial == str(device),
           'Expected a DeviceUtils object with serial %s' % serial)
 
+  def testParallel_noDevices(self):
+    with self.assertCall(
+        mock.call.pylib.device.adb_wrapper.AdbWrapper.GetDevices(), []):
+      with self.assertRaises(device_errors.NoDevicesError):
+        device_utils.DeviceUtils.parallel()
+
 
 if __name__ == '__main__':
   logging.getLogger().setLevel(logging.DEBUG)
diff --git a/build/android/pylib/device/logcat_monitor.py b/build/android/pylib/device/logcat_monitor.py
index 7ede49c..1d4cc24 100644
--- a/build/android/pylib/device/logcat_monitor.py
+++ b/build/android/pylib/device/logcat_monitor.py
@@ -22,18 +22,20 @@
   # Format: <DATE> <TIME> <PID> <TID> <LEVEL> <COMPONENT>: <MESSAGE>
   _THREADTIME_RE_FORMAT = r'\S* +\S* +(%s) +(%s) +(%s) +(%s): +(%s)$'
 
-  def __init__(self, adb, clear=True):
+  def __init__(self, adb, clear=True, filter_specs=None):
     """Create a LogcatMonitor instance.
 
     Args:
       adb: An instance of adb_wrapper.AdbWrapper.
       clear: If True, clear the logcat when monitoring starts.
+      filter_specs: An optional list of '<tag>[:priority]' strings.
     """
     if isinstance(adb, adb_wrapper.AdbWrapper):
       self._adb = adb
     else:
       raise ValueError('Unsupported type passed for argument "device"')
     self._clear = clear
+    self._filter_specs = filter_specs
     self._logcat_out = None
     self._logcat_out_file = None
     self._logcat_proc = None
@@ -76,7 +78,7 @@
     #    returned.
     #  - failure_regex matches a line, in which case None is returned
     #  - the timeout is hit, in which case a CommandTimeoutError is raised.
-    for l in self._adb.Logcat():
+    for l in self._adb.Logcat(filter_specs=self._filter_specs):
       m = success_regex.search(l)
       if m:
         return m
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index 45e6ee4..3f56e6d 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -23,6 +23,10 @@
     os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common'))
 import unittest_util
 
+# Ref: http://developer.android.com/reference/android/app/Activity.html
+_ACTIVITY_RESULT_CANCELED = 0
+_ACTIVITY_RESULT_OK = -1
+
 _DEFAULT_ANNOTATIONS = [
     'Smoke', 'SmallTest', 'MediumTest', 'LargeTest',
     'EnormousTest', 'IntegrationTest']
@@ -54,48 +58,69 @@
   return (code, bundle, statuses)
 
 
-def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms):
-  """Generate the result of |test| from |instr_statuses|.
+def GenerateTestResults(
+    result_code, result_bundle, statuses, start_ms, duration_ms):
+  """Generate test results from |statuses|.
 
   Args:
-    test_name: The name of the test as "class#method"
-    instr_statuses: A list of 2-tuples containing:
+    result_code: The overall status code as an integer.
+    result_bundle: The summary bundle dump as a dict.
+    statuses: A list of 2-tuples containing:
       - the status code as an integer
       - the bundle dump as a dict mapping string keys to string values
       Note that this is the same as the third item in the 3-tuple returned by
       |_ParseAmInstrumentRawOutput|.
     start_ms: The start time of the test in milliseconds.
     duration_ms: The duration of the test in milliseconds.
+
   Returns:
-    An InstrumentationTestResult object.
+    A list containing an instance of InstrumentationTestResult for each test
+    parsed.
   """
-  log = ''
-  result_type = base_test_result.ResultType.UNKNOWN
 
-  for status_code, bundle in instr_statuses:
-    if status_code == instrumentation_parser.STATUS_CODE_START:
-      pass
-    elif status_code == instrumentation_parser.STATUS_CODE_OK:
-      bundle_test = '%s#%s' % (bundle.get('class', ''), bundle.get('test', ''))
-      skipped = bundle.get('test_skipped', '')
+  results = []
 
-      if (test_name == bundle_test and
-          result_type == base_test_result.ResultType.UNKNOWN):
-        result_type = base_test_result.ResultType.PASS
-      elif skipped.lower() in ('true', '1', 'yes'):
-        result_type = base_test_result.ResultType.SKIP
-        logging.info('Skipped ' + test_name)
+  current_result = None
+
+  for status_code, bundle in statuses:
+    test_class = bundle.get('class', '')
+    test_method = bundle.get('test', '')
+    if test_class and test_method:
+      test_name = '%s#%s' % (test_class, test_method)
     else:
-      if status_code not in (instrumentation_parser.STATUS_CODE_ERROR,
-                             instrumentation_parser.STATUS_CODE_FAILURE):
-        logging.error('Unrecognized status code %d. Handling as an error.',
-                      status_code)
-      result_type = base_test_result.ResultType.FAIL
-      if 'stack' in bundle:
-        log = bundle['stack']
+      continue
 
-  return test_result.InstrumentationTestResult(
-      test_name, result_type, start_ms, duration_ms, log=log)
+    if status_code == instrumentation_parser.STATUS_CODE_START:
+      if current_result:
+        results.append(current_result)
+      current_result = test_result.InstrumentationTestResult(
+          test_name, base_test_result.ResultType.UNKNOWN, start_ms, duration_ms)
+    else:
+      if status_code == instrumentation_parser.STATUS_CODE_OK:
+        if bundle.get('test_skipped', '').lower() in ('true', '1', 'yes'):
+          current_result.SetType(base_test_result.ResultType.SKIP)
+        elif current_result.GetType() == base_test_result.ResultType.UNKNOWN:
+          current_result.SetType(base_test_result.ResultType.PASS)
+      else:
+        if status_code not in (instrumentation_parser.STATUS_CODE_ERROR,
+                               instrumentation_parser.STATUS_CODE_FAILURE):
+          logging.error('Unrecognized status code %d. Handling as an error.',
+                        status_code)
+        current_result.SetType(base_test_result.ResultType.FAIL)
+        if 'stack' in bundle:
+          current_result.SetLog(bundle['stack'])
+
+  if current_result:
+    if current_result.GetType() == base_test_result.ResultType.UNKNOWN:
+      crashed = (result_code == _ACTIVITY_RESULT_CANCELED
+                 and any(_NATIVE_CRASH_RE.search(l)
+                         for l in result_bundle.itervalues()))
+      if crashed:
+        current_result.SetType(base_test_result.ResultType.CRASH)
+
+    results.append(current_result)
+
+  return results
 
 
 class InstrumentationTestInstance(test_instance.TestInstance):
@@ -421,37 +446,14 @@
     return inflated_tests
 
   @staticmethod
-  def GenerateMultiTestResult(errors, statuses):
-    results = []
-    skip_counter = 1
-    for status_code, bundle in statuses:
-      if status_code != instrumentation_parser.STATUS_CODE_START:
-        # TODO(rnephew): Make skipped tests still output test name. This is only
-        # there to give skipped tests a unique name so they are counted
-        if 'test_skipped' in bundle:
-          test_name = str(skip_counter)
-          skip_counter += 1
-        else:
-          test_name = '%s#%s' % (bundle.get('class', ''),
-                                 bundle.get('test', ''))
-
-        results.append(
-            GenerateTestResult(test_name, [(status_code, bundle)], 0, 0))
-    for error in errors.itervalues():
-      if _NATIVE_CRASH_RE.search(error):
-        results.append(
-            base_test_result.BaseTestResult(
-            'Crash detected', base_test_result.ResultType.CRASH))
-
-    return results
-
-  @staticmethod
   def ParseAmInstrumentRawOutput(raw_output):
     return ParseAmInstrumentRawOutput(raw_output)
 
   @staticmethod
-  def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms):
-    return GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms)
+  def GenerateTestResults(
+      result_code, result_bundle, statuses, start_ms, duration_ms):
+    return GenerateTestResults(result_code, result_bundle, statuses,
+                               start_ms, duration_ms)
 
   #override
   def TearDown(self):
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance_test.py b/build/android/pylib/instrumentation/instrumentation_test_instance_test.py
index 693f175..752e4d3 100755
--- a/build/android/pylib/instrumentation/instrumentation_test_instance_test.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance_test.py
@@ -27,15 +27,12 @@
     options = mock.Mock()
     options.tool = ''
 
-  def testGenerateTestResult_noStatus(self):
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', [], 0, 1000)
-    self.assertEqual('test.package.TestClass#testMethod', result.GetName())
-    self.assertEqual(base_test_result.ResultType.UNKNOWN, result.GetType())
-    self.assertEqual('', result.GetLog())
-    self.assertEqual(1000, result.GetDuration())
+  def testGenerateTestResults_noStatus(self):
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, [], 0, 1000)
+    self.assertEqual([], results)
 
-  def testGenerateTestResult_testPassed(self):
+  def testGenerateTestResults_testPassed(self):
     statuses = [
       (1, {
         'class': 'test.package.TestClass',
@@ -46,65 +43,52 @@
         'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.PASS, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.PASS, results[0].GetType())
 
-  def testGenerateTestResult_testSkipped_first(self):
-    statuses = [
-      (0, {
-        'test_skipped': 'true',
-      }),
-      (1, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
-      (0, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
-    ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.SKIP, result.GetType())
-
-  def testGenerateTestResult_testSkipped_last(self):
+  def testGenerateTestResults_testSkipped_true(self):
     statuses = [
       (1, {
         'class': 'test.package.TestClass',
         'test': 'testMethod',
       }),
       (0, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
-      (0, {
         'test_skipped': 'true',
+        'class': 'test.package.TestClass',
+        'test': 'testMethod',
+      }),
+      (0, {
+        'class': 'test.package.TestClass',
+        'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.SKIP, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.SKIP, results[0].GetType())
 
-  def testGenerateTestResult_testSkipped_false(self):
+  def testGenerateTestResults_testSkipped_false(self):
     statuses = [
+      (1, {
+        'class': 'test.package.TestClass',
+        'test': 'testMethod',
+      }),
       (0, {
         'test_skipped': 'false',
       }),
-      (1, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
       (0, {
         'class': 'test.package.TestClass',
         'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.PASS, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.PASS, results[0].GetType())
 
-  def testGenerateTestResult_testFailed(self):
+  def testGenerateTestResults_testFailed(self):
     statuses = [
       (1, {
         'class': 'test.package.TestClass',
@@ -115,9 +99,10 @@
         'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.FAIL, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.FAIL, results[0].GetType())
 
 
 if __name__ == '__main__':
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 5f095a5..f3983fd 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -320,9 +320,16 @@
         '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner),
         raw=True, extras=extras, timeout=timeout, retries=3)
 
-  def _GenerateTestResult(self, test, instr_statuses, start_ms, duration_ms):
-    return instrumentation_test_instance.GenerateTestResult(
-        test, instr_statuses, start_ms, duration_ms)
+  def _GenerateTestResult(self, test, instr_result_code, instr_result_bundle,
+                          statuses, start_ms, duration_ms):
+    results = instrumentation_test_instance.GenerateTestResults(
+        instr_result_code, instr_result_bundle, statuses, start_ms, duration_ms)
+    for r in results:
+      if r.GetName() == test:
+        return r
+    logging.error('Could not find result for test: %s', test)
+    return test_result.InstrumentationTestResult(
+        test, base_test_result.ResultType.UNKNOWN, start_ms, duration_ms)
 
   #override
   def RunTest(self, test):
@@ -345,9 +352,10 @@
       duration_ms = time_ms() - start_ms
 
       # Parse the test output
-      _, _, statuses = (
+      result_code, result_bundle, statuses = (
           instrumentation_test_instance.ParseAmInstrumentRawOutput(raw_output))
-      result = self._GenerateTestResult(test, statuses, start_ms, duration_ms)
+      result = self._GenerateTestResult(
+          test, result_code, result_bundle, statuses, start_ms, duration_ms)
       if local_device_instrumentation_test_run.DidPackageCrashOnDevice(
           self.test_pkg.GetPackageName(), self.device):
         result.SetType(base_test_result.ResultType.CRASH)
diff --git a/build/android/pylib/linker/test_case.py b/build/android/pylib/linker/test_case.py
index 8ebf803..c7b0f50 100644
--- a/build/android/pylib/linker/test_case.py
+++ b/build/android/pylib/linker/test_case.py
@@ -123,7 +123,7 @@
   """
 
   # 1. Start recording logcat with appropriate filters.
-  with device.GetLogcatMonitor(filters=_LOGCAT_FILTERS) as logmon:
+  with device.GetLogcatMonitor(filter_specs=_LOGCAT_FILTERS) as logmon:
 
     # 2. Force-start activity.
     device.StartActivity(
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 2ed16ee..ac3f5b1 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -124,16 +124,19 @@
 
     # TODO(jbudorick): Make instrumentation tests output a JSON so this
     # doesn't have to parse the output.
-    logging.info('output from %s:' % test_name)
+    logging.debug('output from %s:', test_name)
     for l in output:
-      logging.info('  %s' % l)
+      logging.debug('  %s', l)
 
-    _, _, statuses = self._test_instance.ParseAmInstrumentRawOutput(output)
-    result = self._test_instance.GenerateTestResult(
-        test_name, statuses, start_ms, duration_ms)
+    result_code, result_bundle, statuses = (
+        self._test_instance.ParseAmInstrumentRawOutput(output))
+    results = self._test_instance.GenerateTestResults(
+        result_code, result_bundle, statuses, start_ms, duration_ms)
     if DidPackageCrashOnDevice(self._test_instance.test_package, device):
-      result.SetType(base_test_result.ResultType.CRASH)
-    return result
+      for r in results:
+        if r.GetType() == base_test_result.ResultType.UNKNOWN:
+          r.SetType(base_test_result.ResultType.CRASH)
+    return results
 
   #override
   def _ShouldShard(self):
diff --git a/build/android/pylib/local/device/local_device_test_run.py b/build/android/pylib/local/device/local_device_test_run.py
index 8c322cb..fa24eb1 100644
--- a/build/android/pylib/local/device/local_device_test_run.py
+++ b/build/android/pylib/local/device/local_device_test_run.py
@@ -70,10 +70,11 @@
     if unknown_tests:
       results.AddResults(
           base_test_result.BaseTestResult(
-              t, base_test_result.ResultType.UNKNOWN)
-          for t in tests)
+              u, base_test_result.ResultType.UNKNOWN)
+          for u in unknown_tests)
     if failed_tests:
       results.AddResults(all_fail_results[f] for f in failed_tests)
+
     return results
 
   def GetTool(self, device):
diff --git a/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py b/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
index 5138d46..709a30c 100644
--- a/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
+++ b/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
@@ -40,10 +40,11 @@
           base_test_result.ResultType.FAIL))
       return r
 
-    _, errors, parsed_output = self._test_instance.ParseAmInstrumentRawOutput(
-        self._results['results']['output'].splitlines())
-    logging.debug(errors)
-    result = self._test_instance.GenerateMultiTestResult(errors, parsed_output)
+    result_code, result_bundle, statuses = (
+        self._test_instance.ParseAmInstrumentRawOutput(
+            self._results['results']['output'].splitlines()))
+    result = self._test_instance.GenerateTestResults(
+        result_code, result_bundle, statuses, 0, 0)
 
     if isinstance(result, base_test_result.BaseTestResult):
       r.AddResult(result)
diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py
index 02f5d6a..d7a4bdf 100644
--- a/build/android/pylib/uiautomator/test_runner.py
+++ b/build/android/pylib/uiautomator/test_runner.py
@@ -79,8 +79,11 @@
     return self.device.RunShellCommand(cmd, timeout=timeout, retries=0)
 
   #override
-  def _GenerateTestResult(self, test, instr_statuses, start_ms, duration_ms):
+  def _GenerateTestResult(self, test, _result_code, _result_bundle, statuses,
+                          start_ms, duration_ms):
     # uiautomator emits its summary status with INSTRUMENTATION_STATUS_CODE,
     # not INSTRUMENTATION_CODE, so we have to drop if off the list of statuses.
+    summary_code, summary_bundle = statuses[-1]
     return super(TestRunner, self)._GenerateTestResult(
-        test, instr_statuses[:-1], start_ms, duration_ms)
+        test, summary_code, summary_bundle, statuses[:-1], start_ms,
+        duration_ms)
diff --git a/build/common.gypi b/build/common.gypi
index 3a071c8..55dad68 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -584,9 +584,6 @@
 
       # If no directory is specified then a temporary directory will be used.
       'test_isolation_outdir%': '',
-      # True if isolate should fail if the isolate files refer to files
-      # that are missing.
-      'test_isolation_fail_on_missing': 1,
 
       'wix_path%': '<(DEPTH)/third_party/wix',
 
@@ -630,6 +627,9 @@
       # Enable LTO on code compiled with -O2.
       'use_lto_o2%': 0,
 
+      # Allowed level of identical code folding in the gold linker.
+      'gold_icf_level%': 'safe',
+
       # Libxkbcommon usage.
       'use_xkbcommon%': 0,
 
@@ -1157,7 +1157,6 @@
     'use_canvas_skia%': '<(use_canvas_skia)',
     'test_isolation_mode%': '<(test_isolation_mode)',
     'test_isolation_outdir%': '<(test_isolation_outdir)',
-    'test_isolation_fail_on_missing': '<(test_isolation_fail_on_missing)',
     'enable_basic_printing%': '<(enable_basic_printing)',
     'enable_print_preview%': '<(enable_print_preview)',
     'enable_spellcheck%': '<(enable_spellcheck)',
@@ -1199,6 +1198,7 @@
     'gomadir%': '<(gomadir)',
     'use_lto%': '<(use_lto)',
     'use_lto_o2%': '<(use_lto_o2)',
+    'gold_icf_level%': '<(gold_icf_level)',
     'video_hole%': '<(video_hole)',
     'support_pre_M6_history_database%': '<(support_pre_M6_history_database)',
     'v8_use_external_startup_data%': '<(v8_use_external_startup_data)',
@@ -1802,9 +1802,6 @@
 
         # Copy it out one scope.
         'android_webview_build%': '<(android_webview_build)',
-
-        # Default android linker script for shared library exports.
-        'android_linker_script%': '<(SHARED_INTERMEDIATE_DIR)/android_exports.lst',
       }],  # OS=="android"
       ['embedded==1', {
         'use_system_fontconfig%': 0,
@@ -1831,7 +1828,6 @@
         'jni_generator_jarjar_file': '../android_webview/build/jarjar-rules.txt',
       }],
       ['OS=="linux" and target_arch!="mipsel"', {
-        # TODO(thakis): This is here to measure perf for a while.
         'clang%': 1,
       }],  # OS=="mac"
       ['OS=="mac"', {
@@ -2144,23 +2140,13 @@
         'clang_chrome_plugins_flags': [
           '<!@(<(DEPTH)/tools/clang/scripts/plugin_flags.sh)'
         ],
-        'conditions': [
-          # TODO(dcheng): https://crbug.com/417463 -- work to enable this flag
-          # on all platforms is currently underway.
-          ['OS=="android" or OS=="linux" or OS=="mac" or OS=="ios"', {
-            'clang_chrome_plugins_flags': [
-              '-Xclang',
-              '-plugin-arg-find-bad-constructs',
-              '-Xclang',
-              'strict-virtual-specifiers',
-            ],
-          }],
-        ],
       }],
       ['asan==1 or msan==1 or lsan==1 or tsan==1', {
         'clang%': 1,
         'use_allocator%': 'none',
         'use_sanitizer_options%': 1,
+        # Disable ICF in the linker to avoid debug info loss.
+        'gold_icf_level%': 'none',
       }],
       ['asan==1 and OS=="linux" and chromeos==0', {
         'use_custom_libcxx%': 1,
@@ -2475,6 +2461,14 @@
         '-Wno-unnamed-type-template-args',
       ],
 
+      # By default, Android targets have their exported JNI symbols stripped,
+      # so we test the manual JNI registration code paths that are required
+      # when using the crazy linker. To allow use of native JNI exports (lazily
+      # resolved by the JVM), targets can enable this variable, which will stop
+      # the stripping from happening. Only targets which do not need to be
+      # compatible with the crazy linker are permitted to set this.
+      'use_native_jni_exports%': 0,
+
       'conditions': [
         ['OS=="win" and component=="shared_library"', {
           # See http://msdn.microsoft.com/en-us/library/aa652367.aspx
@@ -3517,7 +3511,7 @@
         ],
       },
     }],
-    # TODO(thakis): Enable this everywhere. http://crbug.com/371125
+    # -Wl,-z,-defs doesn't work with the sanitiziers, http://crbug.com/452065
     ['(OS=="linux" or OS=="android") and asan==0 and msan==0 and tsan==0 and ubsan==0 and ubsan_vptr==0', {
       'target_defaults': {
         'ldflags': [
@@ -4414,7 +4408,7 @@
                 'target_conditions': [
                   ['_toolset=="target"', {
                     'ldflags': [
-                      '-Wl,--icf=safe',
+                      '-Wl,--icf=<(gold_icf_level)',
                     ],
                   }],
                 ],
@@ -4594,9 +4588,15 @@
               '-Wl,--no-undefined',
             ],
             'conditions': [
-              ['component=="static_library"', {
-                'ldflags': [
-                  '-Wl,--exclude-libs=ALL',
+              ['component=="static_library" and android_webview_build==0', {
+                'target_conditions': [
+                  ['use_native_jni_exports==0', {
+                    # Use a linker version script to strip JNI exports from
+                    # binaries which have not specifically asked to use them.
+                    'ldflags': [
+                      '-Wl,--version-script=<!(cd <(DEPTH) && pwd -P)/build/android/android_no_jni_exports.lst',
+                    ],
+                  }],
                 ],
               }],
               ['clang==1', {
@@ -4656,6 +4656,20 @@
                 'ldflags': [
                   '--sysroot=<(android_ndk_sysroot)',
                   '-nostdlib',
+                  # Don't allow visible symbols from libgcc or stlport to be
+                  # re-exported.
+                  '-Wl,--exclude-libs=libgcc.a',
+                  '-Wl,--exclude-libs=libstlport_static.a',
+                  # Don't allow visible symbols from libraries that contain
+                  # assembly code with symbols that aren't hidden properly.
+                  # http://crbug.com/448386
+                  '-Wl,--exclude-libs=libcommon_audio.a',
+                  '-Wl,--exclude-libs=libcommon_audio_neon.a',
+                  '-Wl,--exclude-libs=libcommon_audio_sse2.a',
+                  '-Wl,--exclude-libs=libiSACFix.a',
+                  '-Wl,--exclude-libs=libisac_neon.a',
+                  '-Wl,--exclude-libs=libopus.a',
+                  '-Wl,--exclude-libs=libvpx.a',
                 ],
                 'libraries': [
                   '-l<(android_stlport_library)',
@@ -4712,7 +4726,7 @@
               ['target_arch == "arm" and order_profiling==0', {
                 'ldflags': [
                   # Enable identical code folding to reduce size.
-                  '-Wl,--icf=safe',
+                  '-Wl,--icf=<(gold_icf_level)',
                 ],
               }],
               # NOTE: The stlport header include paths below are specified in
@@ -4772,9 +4786,6 @@
                 ],
               }],
               ['_type=="shared_library" or _type=="loadable_module"', {
-                'ldflags!': [
-                  '-Wl,--exclude-libs=ALL',
-                ],
                 'ldflags': [
                   '-Wl,-shared,-Bsymbolic',
                 ],
diff --git a/build/compiled_action.gni b/build/compiled_action.gni
index e5059aa..b6d0c4d 100644
--- a/build/compiled_action.gni
+++ b/build/compiled_action.gni
@@ -66,7 +66,7 @@
 # saves unnecessarily compiling your tool for the target platform. But if you
 # need a target build of your tool as well, just leave off the if statement.
 
-if (build_os == "win") {
+if (host_os == "win") {
   _host_executable_suffix = ".exe"
 } else {
   _host_executable_suffix = ""
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 5f93dd9..49b5cfe 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -231,7 +231,7 @@
       # have to tell it to turn it off.
       defines += [ "_HAS_ITERATOR_DEBUGGING=0" ]
     }
-  } else if (is_linux && !is_android && cpu_arch == "x64" &&
+  } else if (is_linux && !is_android && current_cpu == "x64" &&
              !disable_iterator_debugging) {
     # Enable libstdc++ debugging facilities to help catch problems early, see
     # http://crbug.com/65151 .
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index d806a17..cd10730 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -16,7 +16,53 @@
 # KEEP IN ALPHABETICAL ORDER and write a good description for everything.
 # Use "is_*" names for intrinsic platform descriptions and build modes, and
 # "use_*" names for optional features libraries, and configurations.
+
+# TODO(dpranke): The os and cpu_arch variables exist for backwards
+# compatibility and should be deleted once all of the build files and
+# bots have been updated to use current_cpu/target_cpu and
+# current_os/target_os instead.
+
+if (target_os == "") {
+  if (defined(os)) {
+    # If os is defined, it was set in an args file and needs to be
+    # used for backwards-compatibility.
+    target_os = os
+  } else {
+    target_os = host_os
+  }
+}
+
+if (target_cpu == "") {
+  if (defined(cpu_arch)) {
+    # If cpu_arch is defined, it was set in an args file and needs to be
+    # used for backwards-compatibility.
+    target_cpu = cpu_arch
+  } else if (target_os == "android") {
+    # If we're building for Android, we should assume that we want to
+    # build for ARM by default, not the host_cpu (which is likely x64).
+    # This allows us to not have to specify both target_os and target_cpu
+    # on the command line.
+    target_cpu = "arm"
+  } else {
+    target_cpu = host_cpu
+  }
+}
+
+if (current_cpu == "") {
+  current_cpu = target_cpu
+}
+if (current_os == "") {
+  current_os = target_os
+}
+
 declare_args() {
+  # TODO(dpranke): These values are here for backwards compatibility and
+  # should be deleted when all of the builders and configs have been updated.
+  cpu_arch = target_cpu
+  os = target_os
+  build_cpu_arch = host_cpu
+  build_os = host_os
+
   # How many symbols to include in the build. This affects the performance of
   # the build since the symbols are large and dealing with them is slow.
   #   2 means regular build with symbols.
@@ -32,11 +78,12 @@
   is_debug = true
 
   # Whether we're a traditional desktop unix.
-  is_desktop_linux = os == "linux" && os != "chromeos"
+  is_desktop_linux = current_os == "linux" && current_os != "chromeos"
 
   # Set to true when compiling with the Clang compiler. Typically this is used
   # to configure warnings.
-  is_clang = os == "mac" || os == "ios" || os == "linux" || os == "chromeos"
+  is_clang = current_os == "mac" || current_os == "ios" ||
+             current_os == "linux" || current_os == "chromeos"
 
   # Selects the desired build flavor. Official builds get additional
   # processing to prepare for release. Normally you will want to develop and
@@ -60,29 +107,22 @@
   # Compile for Thread Sanitizer to find threading bugs.
   is_tsan = false
 
-  if (os == "chromeos") {
+  if (current_os == "chromeos") {
     # Allows the target toolchain to be injected as arguments. This is needed
     # to support the CrOS build system which supports per-build-configuration
     # toolchains.
     cros_use_custom_toolchain = false
   }
 
-  # TODO(cjhopman): Make target_arch work for all platforms.
-
-  # Architecture of the target device. For Android builds, this will be equal to
-  # the cpu_arch of the default toolchain. When checking the CPU architecture
-  # for source files and build dependencies you should almost alway use cpu_arch
-  # instead. cpu_arch is the architecture of the current toolchain and allows
-  # cross-compiles (compiling the same target for multiple toolchains in the
-  # same build) to work.
-  target_arch = "arm"
-
   # TODO(brettw) remove this flag (and therefore enable linking all targets) on
   # Windows when we have sufficient bot capacity. In the meantime, you can
   # enable linking for local compiles.
   link_chrome_on_windows = true
 }
 
+# TODO(dpranke): Remove these asserts when os and cpu_arch are removed.
+assert(current_os == os)
+
 # =============================================================================
 # OS DEFINITIONS
 # =============================================================================
@@ -98,10 +138,10 @@
 #   generally too different despite being based on the Linux kernel).
 #
 # Do not add more is_* variants here for random lesser-used Unix systems like
-# aix or one of the BSDs. If you need to check these, just check the os value
-# directly.
+# aix or one of the BSDs. If you need to check these, just check the
+# current_os value directly.
 
-if (os == "win") {
+if (current_os == "win") {
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -110,7 +150,7 @@
   is_nacl = false
   is_posix = false
   is_win = true
-} else if (os == "mac") {
+} else if (current_os == "mac") {
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -119,7 +159,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "android") {
+} else if (current_os == "android") {
   is_android = true
   is_chromeos = false
   is_ios = false
@@ -128,7 +168,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "chromeos") {
+} else if (current_os == "chromeos") {
   is_android = false
   is_chromeos = true
   is_ios = false
@@ -137,9 +177,10 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "nacl") {
-  # os == "nacl" will be passed by the nacl toolchain definition. It is not
-  # set by default or on the command line. We treat is as a Posix variant.
+} else if (current_os == "nacl") {
+  # current_os == "nacl" will be passed by the nacl toolchain definition.
+  # It is not set by default or on the command line. We treat is as a
+  # Posix variant.
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -148,7 +189,7 @@
   is_nacl = true
   is_posix = true
   is_win = false
-} else if (os == "ios") {
+} else if (current_os == "ios") {
   is_android = false
   is_chromeos = false
   is_ios = true
@@ -157,7 +198,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "linux") {
+} else if (current_os == "linux") {
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -169,18 +210,6 @@
 }
 
 # =============================================================================
-# CPU ARCHITECTURE
-# =============================================================================
-
-if (is_android) {
-  # TODO(cjhopman): enable this assert once bots are updated to not set
-  # cpu_arch.
-  #assert(cpu_arch == build_cpu_arch, "Android device target architecture should
-  #    be set with 'target_arch', not 'cpu_arch'")
-  cpu_arch = target_arch
-}
-
-# =============================================================================
 # SOURCES FILTERS
 # =============================================================================
 #
@@ -441,6 +470,11 @@
   _shared_library_configs += _windows_linker_configs
 } else if (is_mac) {
   _shared_library_configs += [ "//build/config/mac:mac_dynamic_flags" ]
+} else if (is_android) {
+  # Strip native JNI exports from shared libraries by default. Binaries that
+  # want this can remove this config.
+  _shared_library_configs +=
+      [ "//build/config/android:hide_native_jni_exports" ]
 }
 set_defaults("shared_library") {
   configs = _shared_library_configs
@@ -481,28 +515,28 @@
 
 if (is_win) {
   # On windows we use the same toolchain for host and target by default.
-  # TODO(dpranke): rename the toolchains to x64 and x86 to match cpu_arch.
-  if (cpu_arch == "x64") {
+  # TODO(dpranke): rename the toolchains to x64 and x86 to match current_cpu.
+  if (current_cpu == "x64") {
     host_toolchain = "//build/toolchain/win:64"
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     host_toolchain = "//build/toolchain/win:32"
   }
   set_default_toolchain("$host_toolchain")
 } else if (is_android) {
   # Use clang for the x86/64 Linux host builds.
-  if (build_cpu_arch == "x86" || build_cpu_arch == "x64") {
-    host_toolchain = "//build/toolchain/linux:clang_$build_cpu_arch"
+  if (host_cpu == "x86" || host_cpu == "x64") {
+    host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
   } else {
-    host_toolchain = "//build/toolchain/linux:$build_cpu_arch"
+    host_toolchain = "//build/toolchain/linux:$host_cpu"
   }
-  set_default_toolchain("//build/toolchain/android:$cpu_arch")
+  set_default_toolchain("//build/toolchain/android:$current_cpu")
 } else if (is_linux) {
   if (is_clang) {
-    host_toolchain = "//build/toolchain/linux:clang_$build_cpu_arch"
-    set_default_toolchain("//build/toolchain/linux:clang_$cpu_arch")
+    host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+    set_default_toolchain("//build/toolchain/linux:clang_$current_cpu")
   } else {
-    host_toolchain = "//build/toolchain/linux:$build_cpu_arch"
-    set_default_toolchain("//build/toolchain/linux:$cpu_arch")
+    host_toolchain = "//build/toolchain/linux:$host_cpu"
+    set_default_toolchain("//build/toolchain/linux:$current_cpu")
   }
   if (is_chromeos && cros_use_custom_toolchain) {
     set_default_toolchain("//build/toolchain/cros:target")
diff --git a/build/config/allocator.gni b/build/config/allocator.gni
index 9fcfe49..4c9ae67 100644
--- a/build/config/allocator.gni
+++ b/build/config/allocator.gni
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 # TODO(GYP): Make tcmalloc work on win.
-if (is_android || cpu_arch == "mipsel" || is_mac || is_asan || is_win) {
+if (is_android || current_cpu == "mipsel" || is_mac || is_asan || is_win) {
   _default_allocator = "none"
 } else {
   _default_allocator = "tcmalloc"
diff --git a/build/config/android/BUILD.gn b/build/config/android/BUILD.gn
index 0cc38b7..5492693 100644
--- a/build/config/android/BUILD.gn
+++ b/build/config/android/BUILD.gn
@@ -25,3 +25,8 @@
   cflags = [ "-fPIE" ]
   ldflags = [ "-pie" ]
 }
+
+config("hide_native_jni_exports") {
+  ldflags = [ "-Wl,--version-script=" +
+              rebase_path("//build/android/android_no_jni_exports.lst") ]
+}
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 0105a64..cebf4de 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -50,9 +50,9 @@
 
   # Defines the name the Android build gives to the current host CPU
   # architecture, which is different than the names GN uses.
-  if (build_cpu_arch == "x64") {
+  if (host_cpu == "x64") {
     android_host_arch = "x86_64"
-  } else if (build_cpu_arch == "x86") {
+  } else if (host_cpu == "x86") {
     android_host_arch = "x86"
   } else {
     assert(false, "Need Android toolchain support for your build CPU arch.")
@@ -60,7 +60,7 @@
 
   # Defines the name the Android build gives to the current host CPU
   # architecture, which is different than the names GN uses.
-  if (build_os == "linux") {
+  if (host_os == "linux") {
     android_host_os = "linux"
   } else {
     assert(false, "Need Android toolchain support for your build OS.")
@@ -119,32 +119,32 @@
   # Location of libgcc. This is only needed for the current GN toolchain, so we
   # only need to define the current one, rather than one for every platform
   # like the toolchain roots.
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     android_prebuilt_arch = "android-x86"
     _binary_prefix = "i686-linux-android"
     android_toolchain_root = "$x86_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/i686-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     android_prebuilt_arch = "android-arm"
     _binary_prefix = "arm-linux-androideabi"
     android_toolchain_root = "$arm_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/arm-linux-androideabi/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "mipsel") {
+  } else if (current_cpu == "mipsel") {
     android_prebuilt_arch = "android-mips"
     _binary_prefix = "mipsel-linux-android"
     android_toolchain_root = "$mips_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/mipsel-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     android_prebuilt_arch = "android-x86_64"
     _binary_prefix = "x86_64-linux-android"
     android_toolchain_root = "$x86_64_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/x86_64-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     android_prebuilt_arch = "android-arm64"
     _binary_prefix = "aarch64-linux-android"
     android_toolchain_root = "$arm64_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/aarch64-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "mips64el") {
+  } else if (current_cpu == "mips64el") {
     android_prebuilt_arch = "android-mips64"
     _binary_prefix = "mips64el-linux-android"
     android_toolchain_root = "$mips64_android_toolchain_root"
@@ -169,25 +169,25 @@
 
   # ABI ------------------------------------------------------------------------
 
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     android_app_abi = "x86"
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     import("//build/config/arm.gni")
     if (arm_version < 7) {
       android_app_abi = "armeabi"
     } else {
       android_app_abi = "armeabi-v7a"
     }
-  } else if (cpu_arch == "mipsel") {
+  } else if (current_cpu == "mipsel") {
     android_app_abi = "mips"
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     android_app_abi = "x86_64"
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     android_app_abi = "arm64-v8a"
-  } else if (cpu_arch == "mips64el") {
+  } else if (current_cpu == "mips64el") {
     android_app_abi = "mips64"
   } else {
-    assert(false, "Unknown Android ABI: " + cpu_arch)
+    assert(false, "Unknown Android ABI: " + current_cpu)
   }
 } else {
   if (!defined(is_android_webview_build)) {
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 33dfa37..8ca0642 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -74,6 +74,12 @@
         rebase_path(jni_generator_jarjar_file, root_build_dir),
       ]
     }
+    if (!is_clang) {
+      # Clang builds currently fail with --native_exports_optional due to
+      # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+      # http://crbug.com/442327
+      args += [ "--native_exports_optional" ]
+    }
   }
 
   config("jni_includes_${target_name}") {
@@ -184,6 +190,12 @@
         "--includes",
         rebase_path(jni_generator_include, root_build_dir),
       ]
+      if (!is_clang) {
+        # Clang builds currently fail with --native_exports_optional due to
+        # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+        # http://crbug.com/442327
+        args += [ "--native_exports_optional" ]
+      }
     }
   }
 
@@ -346,9 +358,7 @@
 
   action("${target_name}__generate_enum") {
     # The sources aren't compiled so don't check their dependencies.
-    # TODO(brettw) uncomment after GN binary rolled pas 314974 (which added
-    # support for this value on actions).
-    #check_includes = false
+    check_includes = false
 
     sources = invoker.sources
     script = "//build/android/gyp/java_cpp_enum.py"
diff --git a/build/config/arm.gni b/build/config/arm.gni
index d39c6e9..778ecc1 100644
--- a/build/config/arm.gni
+++ b/build/config/arm.gni
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   declare_args() {
     # Version of the ARM processor when compiling on ARM. Ignored on non-ARM
     # platforms.
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 164b97d..8cb4088 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -3,10 +3,10 @@
 # found in the LICENSE file.
 
 import("//build/config/android/config.gni")
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
-if (cpu_arch == "mipsel" || cpu_arch == "mips64el") {
+if (current_cpu == "mipsel" || current_cpu == "mips64el") {
   import("//build/config/mips.gni")
 }
 if (is_posix) {
@@ -24,7 +24,7 @@
   # These are not multi-arch so cannot be used except on x86 and x86-64 (the
   # only two architectures that are currently checked in). Turn this off when
   # you are using a custom toolchain and need to control -B in cflags.
-  linux_use_bundled_binutils = is_linux && cpu_arch == "x64"
+  linux_use_bundled_binutils = is_linux && current_cpu == "x64"
 
   # Compile in such a way as to enable profiling of the generated code. For
   # example, don't omit the frame pointer and leave in symbols.
@@ -39,7 +39,7 @@
 
   # Use gold for linking on 64-bit Linux only (on 32-bit it runs out of
   # address space, and it doesn't support cross-compiling).
-  use_gold = is_linux && cpu_arch == "x64"
+  use_gold = is_linux && current_cpu == "x64"
 
   # use_debug_fission: whether to use split DWARF debug info
   # files. This can reduce link time significantly, but is incompatible
@@ -121,7 +121,7 @@
     }
 
     # Linker warnings.
-    if (!(is_chromeos && cpu_arch == "arm") && !is_mac) {
+    if (!(is_chromeos && current_cpu == "arm") && !is_mac) {
       # TODO(jochen): Enable this on chromeos on arm. http://crbug.com/356580
       ldflags += [ "-Wl,--fatal-warnings" ]
     }
@@ -172,12 +172,12 @@
     common_mac_flags = []
 
     # CPU architecture.
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       common_mac_flags += [
         "-arch",
         "x86_64",
       ]
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       common_mac_flags += [
         "-arch",
         "i386",
@@ -222,13 +222,13 @@
 
     # CPU architecture. We may or may not be doing a cross compile now, so for
     # simplicity we always explicitly set the architecture.
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       cflags += [
         "-m64",
         "-march=x86-64",
       ]
       ldflags += [ "-m64" ]
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       cflags += [ "-m32" ]
       ldflags += [ "-m32" ]
       if (is_clang) {
@@ -242,7 +242,7 @@
           "-mstackrealign",
         ]
       }
-    } else if (cpu_arch == "arm") {
+    } else if (current_cpu == "arm") {
       # Don't set the compiler flags for the WebView build. These will come
       # from the Android build system.
       if (!is_android_webview_build) {
@@ -275,7 +275,7 @@
           ]
         }
       }
-    } else if (cpu_arch == "mipsel") {
+    } else if (current_cpu == "mipsel") {
       # Don't set the compiler flags for the WebView build. These will come
       # from the Android build system.
       if (!is_android_webview_build) {
@@ -302,7 +302,7 @@
           ]
         }
       }
-    } else if (cpu_arch == "mips64el") {
+    } else if (current_cpu == "mips64el") {
       # Don't set the compiler flags for the WebView build. These will come
       # from the Android build system.
       if (!is_android_webview_build) {
@@ -437,7 +437,7 @@
     }
 
     # Use gold for Android for most CPU architectures.
-    if (cpu_arch == "x86" || cpu_arch == "x64" || cpu_arch == "arm") {
+    if (current_cpu == "x86" || current_cpu == "x64" || current_cpu == "arm") {
       ldflags += [ "-fuse-ld=gold" ]
       if (is_clang) {
         # Let clang find the ld.gold in the NDK.
@@ -449,10 +449,17 @@
     ldflags += [
       "-Wl,--no-undefined",
 
-      # Don't export symbols from statically linked libraries.
-      "-Wl,--exclude-libs=ALL",
+      # Don't allow visible symbols from libgcc or stlport to be
+      # re-exported.
+      "-Wl,--exclude-libs=libgcc.a",
+      "-Wl,--exclude-libs=libstlport_static.a",
+
+      # Don't allow visible symbols from libraries that contain
+      # assembly code with symbols that aren't hidden properly.
+      # http://crbug.com/448386
+      "-Wl,--exclude-libs=libvpx_assembly_arm.a",
     ]
-    if (cpu_arch == "arm") {
+    if (current_cpu == "arm") {
       ldflags += [
         # Enable identical code folding to reduce size.
         "-Wl,--icf=safe",
@@ -460,10 +467,10 @@
     }
 
     if (is_clang) {
-      if (cpu_arch == "arm") {
+      if (current_cpu == "arm") {
         cflags += [ "-target arm-linux-androideabi" ]
         ldflags += [ "-target arm-linux-androideabi" ]
-      } else if (cpu_arch == "x86") {
+      } else if (current_cpu == "x86") {
         cflags += [ "-target x86-linux-androideabi" ]
         ldflags += [ "-target x86-linux-androideabi" ]
       }
@@ -472,7 +479,7 @@
 }
 
 config("compiler_arm_fpu") {
-  if (cpu_arch == "arm" && !is_android_webview_build) {
+  if (current_cpu == "arm" && !is_android_webview_build) {
     cflags = [ "-mfpu=$arm_fpu" ]
   }
 }
@@ -570,7 +577,7 @@
       libs += [ "stlport_static" ]
     }
 
-    if (cpu_arch == "mipsel") {
+    if (current_cpu == "mipsel") {
       libs += [
         # ld linker is used for mips Android, and ld does not accept library
         # absolute path prefixed by "-l"; Since libgcc does not exist in mips
@@ -837,7 +844,7 @@
 
     # Suppress warnings about ABI changes on ARM (Clang doesn't give this
     # warning).
-    if (cpu_arch == "arm" && !is_clang) {
+    if (current_cpu == "arm" && !is_clang) {
       cflags += [ "-Wno-psabi" ]
     }
 
@@ -881,6 +888,20 @@
   }
 }
 
+# On Windows compiling on x64, VC will issue a warning when converting
+# size_t to int because it will truncate the value. Our code should not have
+# these warnings and one should use a static_cast or a checked_cast for the
+# conversion depending on the case. However, a lot of code still needs to be
+# fixed. Apply this config to such targets to disable the warning.
+#
+# Note that this can be applied regardless of platform and architecture to
+# clean up the call sites. This will only apply the flag when necessary.
+config("no_size_t_to_int_warning") {
+  if (is_win && current_cpu == "x64") {
+    cflags = [ "/wd4267" ]
+  }
+}
+
 # Optimization -----------------------------------------------------------------
 #
 # Note that BUILDCONFIG.gn sets up a variable "default_optimization_config"
diff --git a/build/config/features.gni b/build/config/features.gni
index 3811e27..ba69371 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -74,9 +74,9 @@
 # currently.
 # Do not disable seccomp_bpf anywhere without talking to
 # security@chromium.org!
-use_seccomp_bpf =
-    (is_linux || is_android) && (cpu_arch == "x86" || cpu_arch == "x64" ||
-                                 cpu_arch == "arm" || cpu_arch == "mipsel")
+use_seccomp_bpf = (is_linux || is_android) &&
+                  (current_cpu == "x86" || current_cpu == "x64" ||
+                   current_cpu == "arm" || current_cpu == "mipsel")
 
 # Enable notifications everywhere except iOS.
 enable_notifications = !is_ios
diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn
index 0453c84..202fd73 100644
--- a/build/config/linux/BUILD.gn
+++ b/build/config/linux/BUILD.gn
@@ -180,15 +180,17 @@
   #ignore_libs = true  # Loader generated below.
 }
 
-# This generates a target named "gio".
-generate_library_loader("gio") {
-  name = "LibGioLoader"
-  output_h = "libgio.h"
-  output_cc = "libgio_loader.cc"
-  header = "<gio/gio.h>"
-  config = ":gio_config"
+if (is_desktop_linux) {
+  # This generates a target named "gio".
+  generate_library_loader("gio") {
+    name = "LibGioLoader"
+    output_h = "libgio.h"
+    output_cc = "libgio_loader.cc"
+    header = "<gio/gio.h>"
+    config = ":gio_config"
 
-  functions = gypi_values.libgio_functions
+    functions = gypi_values.libgio_functions
+  }
 }
 
 # This generates a target named "libpci".
diff --git a/build/config/linux/pkg_config.gni b/build/config/linux/pkg_config.gni
index 631d60a..34ed1af 100644
--- a/build/config/linux/pkg_config.gni
+++ b/build/config/linux/pkg_config.gni
@@ -43,7 +43,7 @@
     "-s",
     sysroot,
     "-a",
-    cpu_arch,
+    current_cpu,
   ]
 } else if (pkg_config != "") {
   pkg_config_args = [
diff --git a/build/config/mips.gni b/build/config/mips.gni
index f544d94..512552d 100644
--- a/build/config/mips.gni
+++ b/build/config/mips.gni
@@ -3,11 +3,11 @@
 # found in the LICENSE file.
 
 # MIPS arch variant.
-if (cpu_arch == "mipsel") {
+if (current_cpu == "mipsel") {
   declare_args() {
     mips_arch_variant = "r1"
   }
-} else if (cpu_arch == "mips64el") {
+} else if (current_cpu == "mips64el") {
   if (is_android) {
     declare_args() {
       mips_arch_variant = "r6"
diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni
index a9b250c..941c77a 100644
--- a/build/config/sysroot.gni
+++ b/build/config/sysroot.gni
@@ -16,17 +16,17 @@
 } else if (is_android) {
   import("//build/config/android/config.gni")
   if (!is_android_webview_build) {
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       sysroot = rebase_path("$android_ndk_root/$x86_android_sysroot_subdir")
-    } else if (cpu_arch == "arm") {
+    } else if (current_cpu == "arm") {
       sysroot = rebase_path("$android_ndk_root/$arm_android_sysroot_subdir")
-    } else if (cpu_arch == "mipsel") {
+    } else if (current_cpu == "mipsel") {
       sysroot = rebase_path("$android_ndk_root/$mips_android_sysroot_subdir")
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       sysroot = rebase_path("$android_ndk_root/$x86_64_android_sysroot_subdir")
-    } else if (cpu_arch == "arm64") {
+    } else if (current_cpu == "arm64") {
       sysroot = rebase_path("$android_ndk_root/$arm64_android_sysroot_subdir")
-    } else if (cpu_arch == "mips64") {
+    } else if (current_cpu == "mips64") {
       sysroot = rebase_path("$android_ndk_root/$mips64_android_sysroot_subdir")
     } else {
       sysroot = ""
@@ -37,17 +37,17 @@
 } else if (is_linux && is_chrome_branded && is_official_build && !is_chromeos) {
   # For official builds, use the sysroot checked into the internal source repo
   # so that the builds work on older versions of Linux.
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     sysroot =
         rebase_path("//chrome/installer/linux/debian_wheezy_amd64-sysroot")
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     sysroot = rebase_path("//chrome/installer/linux/debian_wheezy_i386-sysroot")
   } else {
     # Any other builds don't use a sysroot.
     sysroot = ""
   }
 } else if (is_linux && !is_chromeos) {
-  if (cpu_arch == "mipsel") {
+  if (current_cpu == "mipsel") {
     sysroot = rebase_path("//mipsel-sysroot/sysroot")
   } else {
     sysroot = ""
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 3f05108..201d45b 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -31,7 +31,7 @@
 
 # Linker flags for Windows SDK setup, this is applied only to EXEs and DLLs.
 config("sdk_link") {
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     ldflags = [ "/MACHINE:X64" ]
     lib_dirs = [
       "$windows_sdk_path\Lib\winv6.3\um\x64",
diff --git a/build/download_sdk_extras.py b/build/download_sdk_extras.py
index 45e7199..d22d885 100755
--- a/build/download_sdk_extras.py
+++ b/build/download_sdk_extras.py
@@ -9,6 +9,8 @@
 bucket named: <dir in SDK extras>_<package name>_<version>.zip. The file will
 be extracted in the android_tools/sdk/extras directory on the test bots. This
 script will not do anything for developers.
+
+TODO(navabi): Move this script (crbug.com/459819).
 """
 
 import json
@@ -54,8 +56,13 @@
     local_zip = '%s/%s' % (SDK_EXTRAS_PATH, package['zip'])
     if not os.path.exists(local_zip):
       package_zip = '%s/%s' % (SDK_EXTRAS_BUCKET, package['zip'])
-      subprocess.check_call(['python', GSUTIL_PATH, '--force-version', '4.7',
-                             'cp', package_zip, local_zip])
+      try:
+        subprocess.check_call(['python', GSUTIL_PATH, '--force-version', '4.7',
+                               'cp', package_zip, local_zip])
+      except AccessDeniedException:
+        print ('WARNING: Bot does not have permission to download SDK packages.'
+               ' If this bot compiles for Android, it may have errors.')
+        return 0
     # Always clean dir and extract zip to ensure correct contents.
     clean_and_extract(package['dir_name'], package['package'], package['zip'])
 
diff --git a/build/get_landmines.py b/build/get_landmines.py
index 7a918c8..d7c98a5 100755
--- a/build/get_landmines.py
+++ b/build/get_landmines.py
@@ -62,6 +62,7 @@
   print 'Clobber to fix missing NaCl gyp dependencies (crbug.com/427427).'
   print 'Another clobber for missing NaCl gyp deps (crbug.com/427427).'
   print 'Clobber to fix GN not picking up increased ID range (crbug.com/444902)'
+  print 'Remove NaCl toolchains from the output dir (crbug.com/456902)'
 
 
 def main():
diff --git a/build/ios/OWNERS b/build/ios/OWNERS
index 1c3e6c8..4caf405 100644
--- a/build/ios/OWNERS
+++ b/build/ios/OWNERS
@@ -1,3 +1,4 @@
 rohitrao@chromium.org
 stuartmorgan@chromium.org
 
+per-file grit_whitelist.txt=*
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index c1edfdf..484f806 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -130,6 +130,7 @@
 IDS_AUTOFILL_ADDRESS_LINE_SEPARATOR
 IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR
 IDS_AUTOFILL_CC_AMEX
+IDS_AUTOFILL_CC_AMEX_SHORT
 IDS_AUTOFILL_CC_DINERS
 IDS_AUTOFILL_CC_DISCOVER
 IDS_AUTOFILL_CC_GENERIC
@@ -719,6 +720,7 @@
 IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE_AND_URL
 IDS_LIBADDRESSINPUT_VILLAGE_TOWNSHIP
 IDS_LIBADDRESSINPUT_ZIP_CODE_LABEL
+IDS_LINK_FROM_CLIPBOARD
 IDS_LOGIN_DIALOG_OK_BUTTON_LABEL
 IDS_LOGIN_DIALOG_PASSWORD_FIELD
 IDS_LOGIN_DIALOG_TITLE
diff --git a/build/isolate.gypi b/build/isolate.gypi
index d7070fe..fbb6d3c 100644
--- a/build/isolate.gypi
+++ b/build/isolate.gypi
@@ -109,9 +109,6 @@
         ["test_isolation_outdir!=''", {
           'action': [ '--isolate-server', '<(test_isolation_outdir)' ],
         }],
-        ['test_isolation_fail_on_missing == 0', {
-          'action': ['--ignore_broken_items'],
-        }],
         ["test_isolation_mode == 'prepare'", {
           'outputs': [
             '<(PRODUCT_DIR)/<(RULE_INPUT_ROOT).isolated.gen.json',
diff --git a/build/jar_file_jni_generator.gypi b/build/jar_file_jni_generator.gypi
index 4c01c8a..9472c10 100644
--- a/build/jar_file_jni_generator.gypi
+++ b/build/jar_file_jni_generator.gypi
@@ -73,5 +73,13 @@
         '<(DEPTH)/build/android/android_exports.gyp:android_exports',
       ],
     }],
+    ['clang==0', {
+      # Clang builds currently fail with --native_exports_optional due to
+      # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+      # http://crbug.com/442327
+      'variables': {
+        'native_exports%': '--native_exports_optional',
+      },
+    }],
   ],
 }
diff --git a/build/jni_generator.gypi b/build/jni_generator.gypi
index 6edc512..853b5f6 100644
--- a/build/jni_generator.gypi
+++ b/build/jni_generator.gypi
@@ -92,6 +92,14 @@
         '<(DEPTH)/build/android/android_exports.gyp:android_exports',
       ],
     }],
+    ['clang==0', {
+      # Clang builds currently fail with --native_exports_optional due to
+      # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+      # http://crbug.com/442327
+      'variables': {
+        'native_exports%': '--native_exports_optional',
+      },
+    }],
   ],
 }
 
diff --git a/build/json_schema_api.gni b/build/json_schema_api.gni
index 83846a2..68a9fdd 100644
--- a/build/json_schema_api.gni
+++ b/build/json_schema_api.gni
@@ -183,6 +183,7 @@
       sources += get_target_outputs(":$schema_generator_name")
       public_deps += [ ":$schema_generator_name" ]
       deps += [ "//tools/json_schema_compiler:generated_api_util" ]
+      configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     }
 
     if (bundle) {
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index 5704f2d..24c24d6 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -69,8 +69,8 @@
 "race:v8::Locker::Initialize\n"
 
 // http://crbug.com/223352
-"race:uprv_malloc_52\n"
-"race:uprv_realloc_52\n"
+"race:uprv_malloc_54\n"
+"race:uprv_realloc_54\n"
 
 // http://crbug.com/239359
 "race:media::TestInputCallback::OnData\n"
@@ -325,6 +325,15 @@
 // https://crbug.com/455665
 "race:mojo::common::*::tick_clock\n"
 
+// https://crbug.com/459429
+"race:randomnessPid\n"
+
+// https://crbug.com/460243
+"race:IPC::ChannelMojoHost::OnClientLaunched\n"
+
+// https://crbug.com/454655
+"race:content::BrowserTestBase::PostTaskToInProcessRendererAndWait\n"
+
 // End of suppressions.
 ;  // Please keep this semicolon.
 
diff --git a/build/secondary/third_party/cacheinvalidation/BUILD.gn b/build/secondary/third_party/cacheinvalidation/BUILD.gn
index 088f89a..feb93d7 100644
--- a/build/secondary/third_party/cacheinvalidation/BUILD.gn
+++ b/build/secondary/third_party/cacheinvalidation/BUILD.gn
@@ -77,17 +77,14 @@
     "src/google/cacheinvalidation/include/types.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   public_configs = [ ":cacheinvalidation_config" ]
 
   deps = [
     "src/google/cacheinvalidation:cacheinvalidation_proto_cpp",
     "//base",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 test("cacheinvalidation_unittests") {
diff --git a/build/secondary/third_party/icu/BUILD.gn b/build/secondary/third_party/icu/BUILD.gn
index 8b64f70..53818c9 100644
--- a/build/secondary/third_party/icu/BUILD.gn
+++ b/build/secondary/third_party/icu/BUILD.gn
@@ -74,6 +74,26 @@
     "source/i18n/chnsecal.cpp",
     "source/i18n/choicfmt.cpp",
     "source/i18n/coleitr.cpp",
+    "source/i18n/collationbasedatabuilder.cpp",
+    "source/i18n/collationbuilder.cpp",
+    "source/i18n/collationcompare.cpp",
+    "source/i18n/collation.cpp",
+    "source/i18n/collationdatabuilder.cpp",
+    "source/i18n/collationdata.cpp",
+    "source/i18n/collationdatareader.cpp",
+    "source/i18n/collationdatawriter.cpp",
+    "source/i18n/collationfastlatinbuilder.cpp",
+    "source/i18n/collationfastlatin.cpp",
+    "source/i18n/collationfcd.cpp",
+    "source/i18n/collationiterator.cpp",
+    "source/i18n/collationkeys.cpp",
+    "source/i18n/collationroot.cpp",
+    "source/i18n/collationrootelements.cpp",
+    "source/i18n/collationruleparser.cpp",
+    "source/i18n/collationsets.cpp",
+    "source/i18n/collationsettings.cpp",
+    "source/i18n/collationtailoring.cpp",
+    "source/i18n/collationweights.cpp",
     "source/i18n/coll.cpp",
     "source/i18n/compactdecimalformat.cpp",
     "source/i18n/coptccal.cpp",
@@ -95,6 +115,7 @@
     "source/i18n/dcfmtsym.cpp",
     "source/i18n/decContext.c",
     "source/i18n/decfmtst.cpp",
+    "source/i18n/decimalformatpattern.cpp",
     "source/i18n/decimfmt.cpp",
     "source/i18n/decNumber.c",
     "source/i18n/digitlst.cpp",
@@ -105,6 +126,7 @@
     "source/i18n/dtrule.cpp",
     "source/i18n/esctrn.cpp",
     "source/i18n/ethpccal.cpp",
+    "source/i18n/filteredbrk.cpp",
     "source/i18n/fmtable_cnv.cpp",
     "source/i18n/fmtable.cpp",
     "source/i18n/format.cpp",
@@ -122,6 +144,7 @@
     "source/i18n/japancal.cpp",
     "source/i18n/locdspnm.cpp",
     "source/i18n/measfmt.cpp",
+    "source/i18n/measunit.cpp",
     "source/i18n/measure.cpp",
     "source/i18n/msgfmt.cpp",
     "source/i18n/name2uni.cpp",
@@ -137,6 +160,7 @@
     "source/i18n/plurfmt.cpp",
     "source/i18n/plurrule.cpp",
     "source/i18n/quant.cpp",
+    "source/i18n/quantityformatter.cpp",
     "source/i18n/rbnf.cpp",
     "source/i18n/rbt.cpp",
     "source/i18n/rbt_data.cpp",
@@ -149,13 +173,17 @@
     "source/i18n/regexst.cpp",
     "source/i18n/regextxt.cpp",
     "source/i18n/region.cpp",
+    "source/i18n/reldatefmt.cpp",
     "source/i18n/reldtfmt.cpp",
     "source/i18n/rematch.cpp",
     "source/i18n/remtrans.cpp",
     "source/i18n/repattrn.cpp",
+    "source/i18n/rulebasedcollator.cpp",
+    "source/i18n/scientificformathelper.cpp",
     "source/i18n/scriptset.cpp",
     "source/i18n/search.cpp",
     "source/i18n/selfmt.cpp",
+    "source/i18n/sharedbreakiterator.cpp",
     "source/i18n/simpletz.cpp",
     "source/i18n/smpdtfmt.cpp",
     "source/i18n/smpdtfst.cpp",
@@ -164,7 +192,6 @@
     "source/i18n/strrepl.cpp",
     "source/i18n/stsearch.cpp",
     "source/i18n/taiwncal.cpp",
-    "source/i18n/tblcoll.cpp",
     "source/i18n/timezone.cpp",
     "source/i18n/titletrn.cpp",
     "source/i18n/tmunit.cpp",
@@ -182,21 +209,17 @@
     "source/i18n/tzrule.cpp",
     "source/i18n/tztrans.cpp",
     "source/i18n/ucal.cpp",
-    "source/i18n/ucln_in.c",
-    "source/i18n/ucol_bld.cpp",
-    "source/i18n/ucol_cnt.cpp",
+    "source/i18n/ucln_in.cpp",
     "source/i18n/ucol.cpp",
     "source/i18n/ucoleitr.cpp",
-    "source/i18n/ucol_elm.cpp",
     "source/i18n/ucol_res.cpp",
     "source/i18n/ucol_sit.cpp",
-    "source/i18n/ucol_tok.cpp",
-    "source/i18n/ucol_wgt.cpp",
     "source/i18n/ucsdet.cpp",
     "source/i18n/ucurr.cpp",
     "source/i18n/udat.cpp",
     "source/i18n/udateintervalformat.cpp",
     "source/i18n/udatpg.cpp",
+    "source/i18n/uitercollationiterator.cpp",
     "source/i18n/ulocdata.c",
     "source/i18n/umsg.cpp",
     "source/i18n/unesctrn.cpp",
@@ -213,6 +236,8 @@
     "source/i18n/uspoof.cpp",
     "source/i18n/uspoof_impl.cpp",
     "source/i18n/uspoof_wsconf.cpp",
+    "source/i18n/utf16collationiterator.cpp",
+    "source/i18n/utf8collationiterator.cpp",
     "source/i18n/utmscale.c",
     "source/i18n/utrans.cpp",
     "source/i18n/vtzone.cpp",
@@ -286,8 +311,9 @@
     "source/common/errorcode.cpp",
     "source/common/filterednormalizer2.cpp",
     "source/common/icudataver.c",
-    "source/common/icuplug.c",
+    "source/common/icuplug.cpp",
     "source/common/listformatter.cpp",
+    "source/common/loadednormalizer2impl.cpp",
     "source/common/locavailable.cpp",
     "source/common/locbased.cpp",
     "source/common/locdispnames.cpp",
@@ -325,6 +351,8 @@
     "source/common/servnotf.cpp",
     "source/common/servrbf.cpp",
     "source/common/servslkf.cpp",
+    "source/common/sharedobject.cpp",
+    "source/common/simplepatternformatter.cpp",
     "source/common/stringpiece.cpp",
     "source/common/stringtriebuilder.cpp",
     "source/common/uarrsort.c",
@@ -342,7 +370,7 @@
     "source/common/ucharstrie.cpp",
     "source/common/ucharstrieiterator.cpp",
     "source/common/uchriter.cpp",
-    "source/common/ucln_cmn.c",
+    "source/common/ucln_cmn.cpp",
     "source/common/ucmndata.c",
     "source/common/ucnv2022.cpp",
     "source/common/ucnv_bld.cpp",
@@ -359,7 +387,7 @@
     "source/common/ucnvisci.c",
     "source/common/ucnvlat1.c",
     "source/common/ucnv_lmb.c",
-    "source/common/ucnvmbcs.c",
+    "source/common/ucnvmbcs.cpp",
     "source/common/ucnvscsu.c",
     "source/common/ucnvsel.cpp",
     "source/common/ucnv_set.c",
@@ -380,11 +408,13 @@
     "source/common/uiter.cpp",
     "source/common/ulist.c",
     "source/common/uloc.cpp",
+    "source/common/uloc_keytype.cpp",
     "source/common/uloc_tag.c",
     "source/common/umapfile.c",
     "source/common/umath.c",
     "source/common/umutex.cpp",
     "source/common/unames.cpp",
+    "source/common/unifiedcache.cpp",
     "source/common/unifilt.cpp",
     "source/common/unifunct.cpp",
     "source/common/uniset_closure.cpp",
@@ -399,7 +429,6 @@
     "source/common/unistr_titlecase_brkiter.cpp",
     "source/common/unormcmp.cpp",
     "source/common/unorm.cpp",
-    "source/common/unorm_it.c",
     "source/common/uobject.cpp",
     "source/common/uprops.cpp",
     "source/common/uresbund.cpp",
@@ -416,7 +445,7 @@
     "source/common/ustack.cpp",
     "source/common/ustrcase.cpp",
     "source/common/ustrcase_locale.cpp",
-    "source/common/ustr_cnv.c",
+    "source/common/ustr_cnv.cpp",
     "source/common/ustrenum.cpp",
     "source/common/ustrfmt.c",
     "source/common/ustring.cpp",
@@ -457,6 +486,7 @@
 
   if (is_win || icu_use_data_file) {
     sources += [ "source/stubdata/stubdata.c" ]
+    defines += [ "U_ICUDATAENTRY_IN_COMMON" ]
   }
 }
 
diff --git a/build/secondary/third_party/libjpeg_turbo/BUILD.gn b/build/secondary/third_party/libjpeg_turbo/BUILD.gn
index be16302..bf35d07 100644
--- a/build/secondary/third_party/libjpeg_turbo/BUILD.gn
+++ b/build/secondary/third_party/libjpeg_turbo/BUILD.gn
@@ -5,17 +5,17 @@
 # Do not use the targets in this file unless you need a certain libjpeg
 # implementation. Use the meta target //third_party:jpeg instead.
 
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
 
-if (cpu_arch == "x86" || cpu_arch == "x64") {
+if (current_cpu == "x86" || current_cpu == "x64") {
   import("//third_party/yasm/yasm_assemble.gni")
 
   yasm_assemble("simd_asm") {
     defines = []
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       sources = [
         "simd/jccolmmx.asm",
         "simd/jccolss2.asm",
@@ -52,7 +52,7 @@
         "simd/jsimdcpu.asm",
       ]
       defines += [ "__x86__" ]
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       sources = [
         "simd/jccolss2-64.asm",
         "simd/jcgrass2-64.asm",
@@ -76,7 +76,7 @@
     if (is_win) {
       defines += [ "MSVC" ]
       include_dirs = [ "win" ]
-      if (cpu_arch == "x86") {
+      if (current_cpu == "x86") {
         defines += [ "WIN32" ]
       } else {
         defines += [ "WIN64" ]
@@ -92,7 +92,7 @@
 }
 
 source_set("simd") {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     deps = [
       ":simd_asm",
     ]
@@ -102,14 +102,14 @@
     if (is_win) {
       cflags = [ "/wd4245" ]
     }
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     deps = [
       ":simd_asm",
     ]
     sources = [
       "simd/jsimd_x86_64.c",
     ]
-  } else if (cpu_arch == "arm" && arm_version >= 7 &&
+  } else if (current_cpu == "arm" && arm_version >= 7 &&
              (arm_use_neon || arm_optionally_use_neon)) {
     sources = [
       "simd/jsimd_arm.c",
diff --git a/build/secondary/third_party/libsrtp/BUILD.gn b/build/secondary/third_party/libsrtp/BUILD.gn
index 506d19f..d9b658e 100644
--- a/build/secondary/third_party/libsrtp/BUILD.gn
+++ b/build/secondary/third_party/libsrtp/BUILD.gn
@@ -45,7 +45,7 @@
     ]
   }
 
-  if (cpu_arch == "x64" || cpu_arch == "x86" || cpu_arch == "arm") {
+  if (current_cpu == "x64" || current_cpu == "x86" || current_cpu == "arm") {
     defines += [
       # TODO(leozwang): CPU_RISC doesn"t work properly on android/arm
       # platform for unknown reasons, need to investigate the root cause
@@ -56,7 +56,7 @@
     ]
   }
 
-  if (cpu_arch == "mipsel") {
+  if (current_cpu == "mipsel") {
     defines += [ "CPU_RISC" ]
   }
 }
diff --git a/build/secondary/third_party/nss/BUILD.gn b/build/secondary/third_party/nss/BUILD.gn
index 768a85d..ee5f112 100644
--- a/build/secondary/third_party/nss/BUILD.gn
+++ b/build/secondary/third_party/nss/BUILD.gn
@@ -217,7 +217,10 @@
         "//build/config/win:lean_and_mean",  # Won"t compile with lean and mean.
       ]
     }
-    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      "//build/config/compiler:no_size_t_to_int_warning",
+    ]
 
     cflags = []
     defines = [
@@ -228,10 +231,7 @@
     include_dirs = [ "nspr/pr/include/private" ]
 
     if (is_win) {
-      cflags = [
-        "/wd4554",  # Check precidence.
-        "/wd4267",  # Conversion from size_t to "type".
-      ]
+      cflags = [ "/wd4554" ]  # Check precidence.
       defines += [
         "XP_PC",
         "WIN32",
@@ -281,9 +281,9 @@
       ]
     }
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       defines += [ "_X86_" ]
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       defines += [ "_AMD64_" ]
     }
 
@@ -474,7 +474,7 @@
     ]
   }
 
-  if (is_win && cpu_arch == "x86") {
+  if (is_win && current_cpu == "x86") {
     source_set("nss_static_avx") {
       sources = [
         "nss/lib/freebl/intel-gcm-wrap.c",
@@ -882,7 +882,10 @@
     if (is_win) {
       configs -= [ "//build/config/win:unicode" ]  # Requires 8-bit mode.
     }
-    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      "//build/config/compiler:no_size_t_to_int_warning",
+    ]
     public_configs = [ ":nss_static_config" ]
 
     cflags = []
@@ -901,10 +904,7 @@
     ]
 
     if (is_win) {
-      cflags += [
-        "/wd4101",  # Unreferenced local variable.
-        "/wd4267",  # Conversion from size_t to "type".
-      ]
+      cflags += [ "/wd4101" ]  # Unreferenced local variable.
     }
 
     if (include_nss_libpkix) {
@@ -1094,7 +1094,7 @@
       defines += [ "NSS_DISABLE_ROOT_CERTS" ]
     }
 
-    if (cpu_arch == "x64" && !is_win) {
+    if (current_cpu == "x64" && !is_win) {
       sources -= [
         "nss/lib/freebl/chacha20/chacha20.c",
         "nss/lib/freebl/poly1305/poly1305.c",
@@ -1139,7 +1139,7 @@
         "WIN95",
       ]
 
-      if (cpu_arch == "x86") {
+      if (current_cpu == "x86") {
         defines += [
           "NSS_X86_OR_X64",
           "NSS_X86",
@@ -1153,7 +1153,7 @@
           "INTEL_GCM",
         ]
         sources -= [ "nss/lib/freebl/mpi/mpi_amd64.c" ]
-      } else if (cpu_arch == "x64") {
+      } else if (current_cpu == "x64") {
         sources -= [
           "nss/lib/freebl/intel-aes-x86-masm.asm",
           "nss/lib/freebl/mpi/mpi_amd64.c",
@@ -1204,7 +1204,7 @@
       "//third_party/sqlite",
     ]
 
-    if (is_win && cpu_arch == "x86") {
+    if (is_win && current_cpu == "x86") {
       deps += [ ":nss_static_avx" ]
     }
   }
diff --git a/build/toolchain/android/BUILD.gn b/build/toolchain/android/BUILD.gn
index e3d950a..53ad506 100644
--- a/build/toolchain/android/BUILD.gn
+++ b/build/toolchain/android/BUILD.gn
@@ -18,7 +18,7 @@
 #      Subdirectory inside of android_ndk_sysroot where libs go.
 #  - tool_prefix
 #      Prefix to be added to the tool names.
-#  - toolchain_cpu_arch
+#  - toolchain_cpu
 #      Same as gcc_toolchain
 template("android_gcc_toolchain") {
   gcc_toolchain(target_name) {
@@ -51,7 +51,7 @@
     ld = cxx
 
     toolchain_os = "android"
-    toolchain_cpu_arch = invoker.toolchain_cpu_arch
+    toolchain_cpu = invoker.toolchain_cpu
 
     # We make the assumption that the gcc_toolchain will produce a soname with
     # the following definition.
@@ -85,7 +85,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$x86_android_toolchain_root/bin/i686-linux-android-"
-  toolchain_cpu_arch = "x86"
+  toolchain_cpu = "x86"
 }
 
 android_gcc_toolchain("arm") {
@@ -93,7 +93,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$arm_android_toolchain_root/bin/arm-linux-androideabi-"
-  toolchain_cpu_arch = "arm"
+  toolchain_cpu = "arm"
 }
 
 android_gcc_toolchain("mipsel") {
@@ -101,7 +101,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$mips_android_toolchain_root/bin/mipsel-linux-android-"
-  toolchain_cpu_arch = "mipsel"
+  toolchain_cpu = "mipsel"
 }
 
 android_gcc_toolchain("x64") {
@@ -109,7 +109,7 @@
   android_ndk_lib_dir = "usr/lib64"
 
   tool_prefix = "$x86_64_android_toolchain_root/bin/x86_64-linux-android-"
-  toolchain_cpu_arch = "x86_64"
+  toolchain_cpu = "x86_64"
 }
 
 android_gcc_toolchain("arm64") {
@@ -117,7 +117,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$arm64_android_toolchain_root/bin/arm-linux-androideabi-"
-  toolchain_cpu_arch = "aarch64"
+  toolchain_cpu = "aarch64"
 }
 
 android_gcc_toolchain("mips64el") {
@@ -125,5 +125,5 @@
   android_ndk_lib_dir = "usr/lib64"
 
   tool_prefix = "$mips64_android_toolchain_root/bin/mipsel-linux-android-"
-  toolchain_cpu_arch = "mipsel64el"
+  toolchain_cpu = "mipsel64el"
 }
diff --git a/build/toolchain/cros/BUILD.gn b/build/toolchain/cros/BUILD.gn
index d360f72..140958b 100644
--- a/build/toolchain/cros/BUILD.gn
+++ b/build/toolchain/cros/BUILD.gn
@@ -29,7 +29,7 @@
   ar = "${cros_target_ar}"
   ld = cxx
 
-  toolchain_cpu_arch = "${cpu_arch}"
+  toolchain_cpu = "${target_cpu}"
   toolchain_os = "linux"
   is_clang = is_clang
 }
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index 5cca299..5f49f68 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -14,10 +14,10 @@
 #  - ar
 #  - ld
 # and the following which is used in the toolchain_args
-#  - toolchain_cpu_arch  (What "cpu_arch" should be set to when invoking a
-#                         build using this toolchain.)
-#  - toolchain_os  (What "os" should be set to when invoking a build using this
-#                   toolchain.)
+#  - toolchain_cpu  (What "current_cpu" should be set to when invoking a
+#                    build using this toolchain.)
+#  - toolchain_os  (What "current_os" should be set to when invoking a
+#                   build using this toolchain.)
 #
 # Optional parameters:
 #  - libs_section_prefix
@@ -40,8 +40,8 @@
     assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value")
     assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
     assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value")
-    assert(defined(invoker.toolchain_cpu_arch),
-           "gcc_toolchain() must specify a \"toolchain_cpu_arch\"")
+    assert(defined(invoker.toolchain_cpu),
+           "gcc_toolchain() must specify a \"toolchain_cpu\"")
     assert(defined(invoker.toolchain_os),
            "gcc_toolchain() must specify a \"toolchain_os\"")
 
@@ -204,8 +204,19 @@
     # When invoking this toolchain not as the default one, these args will be
     # passed to the build. They are ignored when this is the default toolchain.
     toolchain_args() {
-      cpu_arch = invoker.toolchain_cpu_arch
-      os = invoker.toolchain_os
+      current_cpu = invoker.toolchain_cpu
+      current_os = invoker.toolchain_os
+
+      # These values need to be passed through unchanged.
+      target_os = target_os
+      target_cpu = target_cpu
+
+      # TODO(dpranke): These values are here for backwards compatibility and
+      # should be deleted when all of the builders and configs have been
+      # updated.
+      cpu_arch = current_cpu
+      os = current_os
+
       if (defined(invoker.is_clang)) {
         is_clang = invoker.is_clang
       }
diff --git a/build/toolchain/linux/BUILD.gn b/build/toolchain/linux/BUILD.gn
index 1d17772..7bcd9d0 100644
--- a/build/toolchain/linux/BUILD.gn
+++ b/build/toolchain/linux/BUILD.gn
@@ -24,7 +24,7 @@
   ar = "arm-linux-gnueabi-ar"
   ld = cxx
 
-  toolchain_cpu_arch = "arm"
+  toolchain_cpu = "arm"
   toolchain_os = "linux"
   is_clang = false
 }
@@ -43,7 +43,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x86"
+  toolchain_cpu = "x86"
   toolchain_os = "linux"
   is_clang = true
 }
@@ -55,7 +55,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x86"
+  toolchain_cpu = "x86"
   toolchain_os = "linux"
   is_clang = false
 }
@@ -74,7 +74,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x64"
+  toolchain_cpu = "x64"
   toolchain_os = "linux"
   is_clang = true
 }
@@ -86,7 +86,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x64"
+  toolchain_cpu = "x64"
   toolchain_os = "linux"
   is_clang = false
 }
@@ -97,7 +97,7 @@
   ar = "mipsel-linux-gnu-ar"
   ld = cxx
 
-  toolchain_cpu_arch = "mipsel"
+  toolchain_cpu = "mipsel"
   toolchain_os = "linux"
   is_clang = false
 }
diff --git a/build/toolchain/mac/BUILD.gn b/build/toolchain/mac/BUILD.gn
index 0560714..18ee8b8 100644
--- a/build/toolchain/mac/BUILD.gn
+++ b/build/toolchain/mac/BUILD.gn
@@ -194,14 +194,17 @@
     }
 
     toolchain_args() {
-      os = invoker.toolchain_os
+      current_os = invoker.toolchain_os
+
+      # TODO(dpranke): os is here for backwards compatibility.
+      os = current_os
     }
   }
 }
 
 # Toolchain representing the target build (either mac or iOS).
 mac_clang_toolchain("clang") {
-  toolchain_os = os
+  toolchain_os = current_os
 }
 
 # This toolchain provides a way for iOS target compiles to reference targets
diff --git a/build/toolchain/nacl/BUILD.gn b/build/toolchain/nacl/BUILD.gn
index b923608..5fa637c 100644
--- a/build/toolchain/nacl/BUILD.gn
+++ b/build/toolchain/nacl/BUILD.gn
@@ -54,7 +54,7 @@
   toolchain_args() {
     # Override the default OS detection. The build config will set the is_*
     # flags accordingly.
-    os = "nacl"
+    current_os = "nacl"
 
     # Component build not supported in NaCl, since it does not support shared
     # libraries.
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index b8f87e7..e9461a4 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -31,7 +31,7 @@
                                gyp_win_tool_path,
                                windows_sdk_path,
                                visual_studio_runtime_dirs,
-                               cpu_arch,
+                               current_cpu,
                              ],
                              "scope")
 
@@ -43,7 +43,7 @@
 concurrent_links = exec_script("../get_concurrent_links.py", [], "value")
 
 # Parameters:
-#  cpu_arch: cpu_arch to pass as a build arg
+#  current_cpu: current_cpu to pass as a build arg
 #  environment: File name of environment file.
 template("msvc_toolchain") {
   if (defined(invoker.concurrent_links)) {
@@ -62,7 +62,7 @@
                 "copy_dlls",
                 rebase_path(root_build_dir),
                 configuration,
-                invoker.cpu_arch,
+                invoker.current_cpu,
               ])
 
   if (use_goma) {
@@ -200,7 +200,10 @@
     # When invoking this toolchain not as the default one, these args will be
     # passed to the build. They are ignored when this is the default toolchain.
     toolchain_args() {
-      cpu_arch = invoker.cpu_arch
+      current_cpu = invoker.current_cpu
+
+      # TODO(dpranke): cpu_arch is here for backwards compatibility.
+      cpu_arch = current_cpu
     }
   }
 }
@@ -209,16 +212,18 @@
 # get it sorted out how we want to support them both in a single build.
 # Right now only one of these can be enabled at a time because the
 # runtime libraries get copied to root_build_dir and would collide.
-if (cpu_arch == "x86") {
+if (current_cpu == "x86") {
   msvc_toolchain("32") {
     environment = "environment.x86"
-    cpu_arch = "x86"
+
+    current_cpu = "x86"
   }
 }
 
-if (cpu_arch == "x64") {
+if (current_cpu == "x64") {
   msvc_toolchain("64") {
     environment = "environment.x64"
-    cpu_arch = "x64"
+
+    current_cpu = "x64"
   }
 }
diff --git a/build/toolchain/win/midl.gni b/build/toolchain/win/midl.gni
index ce310d1..30bcbf7 100644
--- a/build/toolchain/win/midl.gni
+++ b/build/toolchain/win/midl.gni
@@ -56,10 +56,10 @@
       "$out_dir/$proxy_file",
     ]
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       win_tool_arch = "environment.x86"
       idl_target_platform = "win32"
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       win_tool_arch = "environment.x64"
       idl_target_platform = "x64"
     } else {
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index cc89638..bc9bd1e 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -51,23 +51,23 @@
   return env
 
 
-def _SetupScript(target_arch, sdk_dir):
+def _SetupScript(target_cpu, sdk_dir):
   """Returns a command (with arguments) to be used to set up the
   environment."""
   # Check if we are running in the SDK command line environment and use
-  # the setup script from the SDK if so. |target_arch| should be either
+  # the setup script from the SDK if so. |target_cpu| should be either
   # 'x86' or 'x64'.
-  assert target_arch in ('x86', 'x64')
+  assert target_cpu in ('x86', 'x64')
   if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir:
     return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
-            '/' + target_arch]
+            '/' + target_cpu]
   else:
     # We only support x64-hosted tools.
     # TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual
     # Studio install location from registry.
     return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
                                           'VC/vcvarsall.bat')),
-            'amd64_x86' if target_arch == 'x86' else 'amd64']
+            'amd64_x86' if target_cpu == 'x86' else 'amd64']
 
 
 def _FormatAsEnvironmentBlock(envvar_dict):
@@ -100,25 +100,25 @@
   if len(sys.argv) != 6:
     print('Usage setup_toolchain.py '
           '<visual studio path> <win tool path> <win sdk path> '
-          '<runtime dirs> <cpu_arch>')
+          '<runtime dirs> <target_cpu>')
     sys.exit(2)
   tool_source = sys.argv[2]
   win_sdk_path = sys.argv[3]
   runtime_dirs = sys.argv[4]
-  cpu_arch = sys.argv[5]
+  target_cpu = sys.argv[5]
 
   _CopyTool(tool_source)
 
-  archs = ('x86', 'x64')
-  assert cpu_arch in archs
+  cpus = ('x86', 'x64')
+  assert target_cpu in cpus
   vc_bin_dir = ''
 
   # TODO(scottmg|goma): Do we need an equivalent of
   # ninja_use_custom_environment_files?
 
-  for arch in archs:
+  for cpu in cpus:
     # Extract environment variables for subprocesses.
-    args = _SetupScript(arch, win_sdk_path)
+    args = _SetupScript(cpu, win_sdk_path)
     args.extend(('&&', 'set'))
     popen = subprocess.Popen(
         args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -126,7 +126,7 @@
     env = _ExtractImportantEnvironment(variables)
     env['PATH'] = runtime_dirs + ';' + env['PATH']
 
-    if arch == cpu_arch:
+    if cpu == target_cpu:
       for path in env['PATH'].split(os.pathsep):
         if os.path.exists(os.path.join(path, 'cl.exe')):
           vc_bin_dir = os.path.realpath(path)
@@ -143,7 +143,7 @@
                                   sdk_dir=win_sdk_path)
       env['INCLUDE'] = additional_includes + env['INCLUDE']
     env_block = _FormatAsEnvironmentBlock(env)
-    with open('environment.' + arch, 'wb') as f:
+    with open('environment.' + cpu, 'wb') as f:
       f.write(env_block)
 
   assert vc_bin_dir
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 6f49e7c..5b175eb 100644
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -129,11 +129,11 @@
                        source_x64)
 
 
-def CopyDlls(target_dir, configuration, cpu_arch):
+def CopyDlls(target_dir, configuration, target_cpu):
   """Copy the VS runtime DLLs into the requested directory as needed.
 
   configuration is one of 'Debug' or 'Release'.
-  cpu_arch is one of 'x86' or 'x64'.
+  target_cpu is one of 'x86' or 'x64'.
 
   The debug configuration gets both the debug and release DLLs; the
   release config only the latter.
@@ -143,7 +143,7 @@
     return
 
   x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
-  runtime_dir = x64_runtime if cpu_arch == 'x64' else x86_runtime
+  runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
   _CopyRuntime(target_dir, runtime_dir, 'msvc%s120.dll')
   if configuration == 'Debug':
     _CopyRuntime(target_dir, runtime_dir, 'msvc%s120d.dll')
diff --git a/build/win/install-build-deps.py b/build/win/install-build-deps.py
deleted file mode 100755
index d9e50b6..0000000
--- a/build/win/install-build-deps.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import shutil
-import sys
-import os
-
-def patch_msbuild():
-  """VS2010 MSBuild has a ULDI bug that we patch here. See http://goo.gl/Pn8tj.
-  """
-  source_path = os.path.join(os.environ['ProgramFiles(x86)'],
-                             "MSBuild",
-                             "Microsoft.Cpp",
-                             "v4.0",
-                             "Microsoft.CppBuild.targets")
-  backup_path = source_path + ".backup"
-  if not os.path.exists(backup_path):
-    try:
-      print "Backing up %s..." % source_path
-      shutil.copyfile(source_path, backup_path)
-    except IOError:
-      print "Could not back up %s to %s. Run as Administrator?" % (
-          source_path, backup_path)
-      return 1
-
-  source = open(source_path).read()
-  base = ('''<Target Name="GetResolvedLinkObjs" Returns="@(ObjFullPath)" '''
-          '''DependsOnTargets="$(CommonBuildOnlyTargets);ComputeCLOutputs;'''
-          '''ResolvedLinkObjs"''')
-  find = base + '>'
-  replace = base + ''' Condition="'$(ConfigurationType)'=='StaticLibrary'">'''
-  result = source.replace(find, replace)
-
-  if result != source:
-    open(source_path, "w").write(result)
-    print "Patched %s." % source_path
-  return 0
-
-
-def main():
-  return patch_msbuild()
-
-
-if __name__ == "__main__":
-  sys.exit(main())
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index d6fd028..cffee57 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -26,8 +26,8 @@
     "animation/layer_animation_value_provider.h",
     "animation/scroll_offset_animation_curve.cc",
     "animation/scroll_offset_animation_curve.h",
-    "animation/scrollbar_animation_controller.h",
     "animation/scrollbar_animation_controller.cc",
+    "animation/scrollbar_animation_controller.h",
     "animation/scrollbar_animation_controller_linear_fade.cc",
     "animation/scrollbar_animation_controller_linear_fade.h",
     "animation/scrollbar_animation_controller_thinning.cc",
@@ -94,12 +94,12 @@
     "debug/layer_tree_debug_state.h",
     "debug/micro_benchmark.cc",
     "debug/micro_benchmark.h",
-    "debug/micro_benchmark_impl.cc",
-    "debug/micro_benchmark_impl.h",
     "debug/micro_benchmark_controller.cc",
     "debug/micro_benchmark_controller.h",
     "debug/micro_benchmark_controller_impl.cc",
     "debug/micro_benchmark_controller_impl.h",
+    "debug/micro_benchmark_impl.cc",
+    "debug/micro_benchmark_impl.h",
     "debug/paint_time_counter.cc",
     "debug/paint_time_counter.h",
     "debug/picture_debug_util.cc",
@@ -125,10 +125,10 @@
     "debug/unittest_only_benchmark_impl.h",
     "input/input_handler.cc",
     "input/input_handler.h",
-    "input/page_scale_animation.cc",
-    "input/page_scale_animation.h",
     "input/layer_selection_bound.cc",
     "input/layer_selection_bound.h",
+    "input/page_scale_animation.cc",
+    "input/page_scale_animation.h",
     "input/scroll_elasticity_helper.cc",
     "input/scroll_elasticity_helper.h",
     "input/selection_bound_type.h",
@@ -301,8 +301,8 @@
     "quads/draw_quad.h",
     "quads/io_surface_draw_quad.cc",
     "quads/io_surface_draw_quad.h",
-    "quads/largest_draw_quad.h",
     "quads/largest_draw_quad.cc",
+    "quads/largest_draw_quad.h",
     "quads/list_container.cc",
     "quads/list_container.h",
     "quads/picture_draw_quad.cc",
@@ -329,10 +329,10 @@
     "quads/yuv_video_draw_quad.h",
     "resources/bitmap_content_layer_updater.cc",
     "resources/bitmap_content_layer_updater.h",
-    "resources/bitmap_tile_task_worker_pool.cc",
-    "resources/bitmap_tile_task_worker_pool.h",
     "resources/bitmap_skpicture_content_layer_updater.cc",
     "resources/bitmap_skpicture_content_layer_updater.h",
+    "resources/bitmap_tile_task_worker_pool.cc",
+    "resources/bitmap_tile_task_worker_pool.h",
     "resources/clip_display_item.cc",
     "resources/clip_display_item.h",
     "resources/clip_path_display_item.cc",
@@ -396,19 +396,13 @@
     "resources/raster_source.h",
     "resources/raster_source_helper.cc",
     "resources/raster_source_helper.h",
+    "resources/raster_tile_priority_queue.cc",
+    "resources/raster_tile_priority_queue.h",
     "resources/raster_tile_priority_queue_all.cc",
     "resources/raster_tile_priority_queue_all.h",
     "resources/raster_tile_priority_queue_required.cc",
     "resources/raster_tile_priority_queue_required.h",
-    "resources/raster_tile_priority_queue.cc",
-    "resources/raster_tile_priority_queue.h",
     "resources/rasterizer.h",
-    "resources/software_rasterizer.cc",
-    "resources/software_rasterizer.h",
-    "resources/tile_task_worker_pool.cc",
-    "resources/tile_task_worker_pool.h",
-    "resources/tile_task_runner.cc",
-    "resources/tile_task_runner.h",
     "resources/release_callback.h",
     "resources/resource.cc",
     "resources/resource.h",
@@ -440,6 +434,8 @@
     "resources/single_release_callback_impl.h",
     "resources/skpicture_content_layer_updater.cc",
     "resources/skpicture_content_layer_updater.h",
+    "resources/software_rasterizer.cc",
+    "resources/software_rasterizer.h",
     "resources/task_graph_runner.cc",
     "resources/task_graph_runner.h",
     "resources/texture_mailbox.cc",
@@ -456,6 +452,10 @@
     "resources/tile_manager.h",
     "resources/tile_priority.cc",
     "resources/tile_priority.h",
+    "resources/tile_task_runner.cc",
+    "resources/tile_task_runner.h",
+    "resources/tile_task_worker_pool.cc",
+    "resources/tile_task_worker_pool.h",
     "resources/tiling_set_eviction_queue.cc",
     "resources/tiling_set_eviction_queue.h",
     "resources/tiling_set_raster_queue_all.cc",
@@ -527,10 +527,8 @@
     "trees/tree_synchronizer.h",
   ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t -> int
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   public_deps = [
     "//skia",
@@ -562,6 +560,8 @@
     "test/animation_test_common.h",
     "test/begin_frame_args_test.cc",
     "test/begin_frame_args_test.h",
+    "test/failure_output_surface.cc",
+    "test/failure_output_surface.h",
     "test/fake_content_layer.cc",
     "test/fake_content_layer.h",
     "test/fake_content_layer_client.cc",
@@ -614,12 +614,8 @@
     "test/fake_tile_manager_client.h",
     "test/fake_ui_resource_layer_tree_host_impl.cc",
     "test/fake_ui_resource_layer_tree_host_impl.h",
-    "test/failure_output_surface.cc",
-    "test/failure_output_surface.h",
     "test/geometry_test_utils.cc",
     "test/geometry_test_utils.h",
-    "test/test_in_process_context_provider.cc",
-    "test/test_in_process_context_provider.h",
     "test/impl_side_painting_settings.h",
     "test/layer_test_common.cc",
     "test/layer_test_common.h",
@@ -670,6 +666,8 @@
     "test/test_gpu_memory_buffer_manager.h",
     "test/test_image_factory.cc",
     "test/test_image_factory.h",
+    "test/test_in_process_context_provider.cc",
+    "test/test_in_process_context_provider.h",
     "test/test_now_source.cc",
     "test/test_now_source.h",
     "test/test_occlusion_tracker.h",
@@ -685,6 +683,8 @@
     "test/tiled_layer_test_common.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   include_dirs = [
     ".",
     "test",
@@ -744,8 +744,8 @@
       "layers/delegated_frame_resource_collection_unittest.cc",
       "layers/delegated_renderer_layer_impl_unittest.cc",
       "layers/delegated_renderer_layer_unittest.cc",
-      "layers/heads_up_display_unittest.cc",
       "layers/heads_up_display_layer_impl_unittest.cc",
+      "layers/heads_up_display_unittest.cc",
       "layers/io_surface_layer_impl_unittest.cc",
       "layers/layer_impl_unittest.cc",
       "layers/layer_iterator_unittest.cc",
@@ -759,15 +759,15 @@
       "layers/picture_image_layer_unittest.cc",
       "layers/picture_layer_impl_unittest.cc",
       "layers/picture_layer_unittest.cc",
-      "layers/render_surface_unittest.cc",
       "layers/render_surface_impl_unittest.cc",
+      "layers/render_surface_unittest.cc",
       "layers/scrollbar_layer_unittest.cc",
       "layers/solid_color_layer_impl_unittest.cc",
       "layers/solid_color_scrollbar_layer_impl_unittest.cc",
-      "layers/surface_layer_unittest.cc",
       "layers/surface_layer_impl_unittest.cc",
-      "layers/texture_layer_unittest.cc",
+      "layers/surface_layer_unittest.cc",
       "layers/texture_layer_impl_unittest.cc",
+      "layers/texture_layer_unittest.cc",
       "layers/tiled_layer_impl_unittest.cc",
       "layers/tiled_layer_unittest.cc",
       "layers/ui_resource_layer_impl_unittest.cc",
@@ -792,8 +792,8 @@
       "resources/picture_pile_impl_unittest.cc",
       "resources/picture_pile_unittest.cc",
       "resources/picture_unittest.cc",
+      "resources/platform_color_unittest.cc",
       "resources/prioritized_resource_unittest.cc",
-      "resources/tile_task_worker_pool_unittest.cc",
       "resources/resource_provider_unittest.cc",
       "resources/resource_update_controller_unittest.cc",
       "resources/scoped_gpu_raster_unittest.cc",
@@ -803,6 +803,7 @@
       "resources/texture_uploader_unittest.cc",
       "resources/tile_manager_unittest.cc",
       "resources/tile_priority_unittest.cc",
+      "resources/tile_task_worker_pool_unittest.cc",
       "scheduler/begin_frame_source_unittest.cc",
       "scheduler/delay_based_time_source_unittest.cc",
       "scheduler/scheduler_state_machine_unittest.cc",
@@ -817,7 +818,6 @@
       "trees/layer_tree_host_pixeltest_blending.cc",
       "trees/layer_tree_host_pixeltest_filters.cc",
       "trees/layer_tree_host_pixeltest_masks.cc",
-      "trees/layer_tree_host_pixeltest_on_demand_raster.cc",
       "trees/layer_tree_host_pixeltest_readback.cc",
       "trees/layer_tree_host_pixeltest_synchronous.cc",
       "trees/layer_tree_host_unittest.cc",
@@ -826,8 +826,8 @@
       "trees/layer_tree_host_unittest_copyrequest.cc",
       "trees/layer_tree_host_unittest_damage.cc",
       "trees/layer_tree_host_unittest_delegated.cc",
-      "trees/layer_tree_host_unittest_occlusion.cc",
       "trees/layer_tree_host_unittest_no_message_loop.cc",
+      "trees/layer_tree_host_unittest_occlusion.cc",
       "trees/layer_tree_host_unittest_picture.cc",
       "trees/layer_tree_host_unittest_proxy.cc",
       "trees/layer_tree_host_unittest_scroll.cc",
@@ -845,10 +845,12 @@
       "surfaces/surfaces_pixeltest.cc",
 
       # Setup.
-      "test/run_all_unittests.cc",
       "test/cc_test_suite.cc",
+      "test/run_all_unittests.cc",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":cc",
       ":test_support",
@@ -876,9 +878,9 @@
     "layers/picture_layer_impl_perftest.cc",
     "resources/picture_layer_tiling_perftest.cc",
     "resources/picture_pile_impl_perftest.cc",
-    "resources/tile_task_worker_pool_perftest.cc",
     "resources/task_graph_runner_perftest.cc",
     "resources/tile_manager_perftest.cc",
+    "resources/tile_task_worker_pool_perftest.cc",
     "test/cc_test_suite.cc",
     "test/run_all_perftests.cc",
     "trees/layer_tree_host_common_perftest.cc",
@@ -886,6 +888,8 @@
     "trees/occlusion_tracker_perftest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":cc",
     ":test_support",
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc
index 48ebc74..96cb40a 100644
--- a/cc/animation/animation.cc
+++ b/cc/animation/animation.cc
@@ -14,32 +14,28 @@
 namespace {
 
 // This should match the RunState enum.
-static const char* const s_runStateNames[] = {
-  "WaitingForTargetAvailability",
-  "WaitingForDeletion",
-  "Starting",
-  "Running",
-  "Paused",
-  "Finished",
-  "Aborted"
-};
+static const char* const s_runStateNames[] = {"WAITING_FOR_TARGET_AVAILABILITY",
+                                              "WAITING_FOR_DELETION",
+                                              "STARTING",
+                                              "RUNNING",
+                                              "PAUSED",
+                                              "FINISHED",
+                                              "ABORTED"};
 
-static_assert(static_cast<int>(cc::Animation::RunStateEnumSize) ==
-              arraysize(s_runStateNames),
+static_assert(static_cast<int>(cc::Animation::LAST_RUN_STATE) + 1 ==
+                  arraysize(s_runStateNames),
               "RunStateEnumSize should equal the number of elements in "
               "s_runStateNames");
 
 // This should match the TargetProperty enum.
-static const char* const s_targetPropertyNames[] = {
-  "Transform",
-  "Opacity",
-  "Filter",
-  "ScrollOffset",
-  "BackgroundColor"
-};
+static const char* const s_targetPropertyNames[] = {"TRANSFORM",
+                                                    "OPACITY",
+                                                    "FILTER",
+                                                    "SCROLL_OFFSET",
+                                                    "BACKGROUND_COLOR"};
 
-static_assert(static_cast<int>(cc::Animation::TargetPropertyEnumSize) ==
-              arraysize(s_targetPropertyNames),
+static_assert(static_cast<int>(cc::Animation::LAST_TARGET_PROPERTY) + 1 ==
+                  arraysize(s_targetPropertyNames),
               "TargetPropertyEnumSize should equal the number of elements in "
               "s_targetPropertyNames");
 
@@ -65,12 +61,12 @@
       id_(animation_id),
       group_(group_id),
       target_property_(target_property),
-      run_state_(WaitingForTargetAvailability),
+      run_state_(WAITING_FOR_TARGET_AVAILABILITY),
       iterations_(1),
       iteration_start_(0),
-      direction_(Normal),
+      direction_(DIRECTION_NORMAL),
       playback_rate_(1),
-      fill_mode_(FillModeBoth),
+      fill_mode_(FILL_MODE_BOTH),
       needs_synchronized_start_time_(false),
       received_finished_event_(false),
       suspended_(false),
@@ -81,8 +77,8 @@
 }
 
 Animation::~Animation() {
-  if (run_state_ == Running || run_state_ == Paused)
-    SetRunState(Aborted, base::TimeTicks());
+  if (run_state_ == RUNNING || run_state_ == PAUSED)
+    SetRunState(ABORTED, base::TimeTicks());
 }
 
 void Animation::SetRunState(RunState run_state,
@@ -97,10 +93,10 @@
                  s_targetPropertyNames[target_property_],
                  group_);
 
-  bool is_waiting_to_start = run_state_ == WaitingForTargetAvailability ||
-                             run_state_ == Starting;
+  bool is_waiting_to_start =
+      run_state_ == WAITING_FOR_TARGET_AVAILABILITY || run_state_ == STARTING;
 
-  if (is_controlling_instance_ && is_waiting_to_start && run_state == Running) {
+  if (is_controlling_instance_ && is_waiting_to_start && run_state == RUNNING) {
     TRACE_EVENT_ASYNC_BEGIN1(
         "cc", "Animation", this, "Name", TRACE_STR_COPY(name_buffer));
   }
@@ -109,9 +105,9 @@
 
   const char* old_run_state_name = s_runStateNames[run_state_];
 
-  if (run_state == Running && run_state_ == Paused)
+  if (run_state == RUNNING && run_state_ == PAUSED)
     total_paused_time_ += (monotonic_time - pause_time_);
-  else if (run_state == Paused)
+  else if (run_state == PAUSED)
     pause_time_ = monotonic_time;
   run_state_ = run_state;
 
@@ -137,13 +133,13 @@
 }
 
 void Animation::Suspend(base::TimeTicks monotonic_time) {
-  SetRunState(Paused, monotonic_time);
+  SetRunState(PAUSED, monotonic_time);
   suspended_ = true;
 }
 
 void Animation::Resume(base::TimeTicks monotonic_time) {
   suspended_ = false;
-  SetRunState(Running, monotonic_time);
+  SetRunState(RUNNING, monotonic_time);
 }
 
 bool Animation::IsFinishedAt(base::TimeTicks monotonic_time) const {
@@ -156,7 +152,7 @@
   if (playback_rate_ == 0)
     return false;
 
-  return run_state_ == Running && iterations_ >= 0 &&
+  return run_state_ == RUNNING && iterations_ >= 0 &&
          TimeUtil::Scale(curve_->Duration(),
                          iterations_ / std::abs(playback_rate_)) <=
              (monotonic_time + time_offset_ - start_time_ - total_paused_time_);
@@ -164,7 +160,7 @@
 
 bool Animation::InEffect(base::TimeTicks monotonic_time) const {
   return ConvertToActiveTime(monotonic_time) >= base::TimeDelta() ||
-         (fill_mode_ == FillModeBoth || fill_mode_ == FillModeBackwards);
+         (fill_mode_ == FILL_MODE_BOTH || fill_mode_ == FILL_MODE_BACKWARDS);
 }
 
 base::TimeDelta Animation::ConvertToActiveTime(
@@ -172,7 +168,7 @@
   base::TimeTicks trimmed = monotonic_time + time_offset_;
 
   // If we're paused, time is 'stuck' at the pause time.
-  if (run_state_ == Paused)
+  if (run_state_ == PAUSED)
     trimmed = pause_time_;
 
   // Returned time should always be relative to the start time and should
@@ -181,7 +177,7 @@
 
   // If we're just starting or we're waiting on receiving a start time,
   // time is 'stuck' at the initial state.
-  if ((run_state_ == Starting && !has_set_start_time()) ||
+  if ((run_state_ == STARTING && !has_set_start_time()) ||
       needs_synchronized_start_time())
     trimmed = base::TimeTicks() + time_offset_;
 
@@ -247,9 +243,10 @@
 
   // Check if we are running the animation in reverse direction for the current
   // iteration
-  bool reverse = (direction_ == Reverse) ||
-                 (direction_ == Alternate && iteration % 2 == 1) ||
-                 (direction_ == AlternateReverse && iteration % 2 == 0);
+  bool reverse =
+      (direction_ == DIRECTION_REVERSE) ||
+      (direction_ == DIRECTION_ALTERNATE && iteration % 2 == 1) ||
+      (direction_ == DIRECTION_ALTERNATE_REVERSE && iteration % 2 == 0);
 
   // If we are running the animation in reverse direction, reverse the result
   if (reverse)
@@ -280,8 +277,8 @@
 void Animation::PushPropertiesTo(Animation* other) const {
   // Currently, we only push changes due to pausing and resuming animations on
   // the main thread.
-  if (run_state_ == Animation::Paused ||
-      other->run_state_ == Animation::Paused) {
+  if (run_state_ == Animation::PAUSED ||
+      other->run_state_ == Animation::PAUSED) {
     other->run_state_ = run_state_;
     other->pause_time_ = pause_time_;
     other->total_paused_time_ = total_paused_time_;
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 9342f1a..2677fde 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -19,43 +19,48 @@
 // loop count, last pause time, and the total time spent paused.
 class CC_EXPORT Animation {
  public:
-  // Animations begin in the 'WaitingForTargetAvailability' state. An Animation
-  // waiting for target availibility will run as soon as its target property
-  // is free (and all the animations animating with it are also able to run).
-  // When this time arrives, the controller will move the animation into the
-  // Starting state, and then into the Running state. Running animations may
-  // toggle between Running and Paused, and may be stopped by moving into either
-  // the Aborted or Finished states. A Finished animation was allowed to run to
-  // completion, but an Aborted animation was not.
+  // Animations begin in the 'WAITING_FOR_TARGET_AVAILABILITY' state. An
+  // Animation waiting for target availibility will run as soon as its target
+  // property is free (and all the animations animating with it are also able to
+  // run). When this time arrives, the controller will move the animation into
+  // the STARTING state, and then into the RUNNING state. RUNNING animations may
+  // toggle between RUNNING and PAUSED, and may be stopped by moving into either
+  // the ABORTED or FINISHED states. A FINISHED animation was allowed to run to
+  // completion, but an ABORTED animation was not.
   enum RunState {
-    WaitingForTargetAvailability = 0,
-    WaitingForDeletion,
-    Starting,
-    Running,
-    Paused,
-    Finished,
-    Aborted,
+    WAITING_FOR_TARGET_AVAILABILITY = 0,
+    WAITING_FOR_DELETION,
+    STARTING,
+    RUNNING,
+    PAUSED,
+    FINISHED,
+    ABORTED,
     // This sentinel must be last.
-    RunStateEnumSize
+    LAST_RUN_STATE = ABORTED
   };
 
   enum TargetProperty {
-    Transform = 0,
-    Opacity,
-    Filter,
-    ScrollOffset,
-    BackgroundColor,
+    TRANSFORM = 0,
+    OPACITY,
+    FILTER,
+    SCROLL_OFFSET,
+    BACKGROUND_COLOR,
     // This sentinel must be last.
-    TargetPropertyEnumSize
+    LAST_TARGET_PROPERTY = BACKGROUND_COLOR
   };
 
-  enum Direction { Normal, Reverse, Alternate, AlternateReverse };
+  enum Direction {
+    DIRECTION_NORMAL,
+    DIRECTION_REVERSE,
+    DIRECTION_ALTERNATE,
+    DIRECTION_ALTERNATE_REVERSE
+  };
 
   enum FillMode {
-    FillModeNone,
-    FillModeForwards,
-    FillModeBackwards,
-    FillModeBoth
+    FILL_MODE_NONE,
+    FILL_MODE_FORWARDS,
+    FILL_MODE_BACKWARDS,
+    FILL_MODE_BOTH
   };
 
   static scoped_ptr<Animation> Create(scoped_ptr<AnimationCurve> curve,
@@ -111,9 +116,8 @@
 
   bool IsFinishedAt(base::TimeTicks monotonic_time) const;
   bool is_finished() const {
-    return run_state_ == Finished ||
-        run_state_ == Aborted ||
-        run_state_ == WaitingForDeletion;
+    return run_state_ == FINISHED || run_state_ == ABORTED ||
+           run_state_ == WAITING_FOR_DELETION;
   }
 
   bool InEffect(base::TimeTicks monotonic_time) const;
@@ -131,7 +135,7 @@
     needs_synchronized_start_time_ = needs_synchronized_start_time;
   }
 
-  // This is true for animations running on the main thread when the Finished
+  // This is true for animations running on the main thread when the FINISHED
   // event sent by the corresponding impl animation has been received.
   bool received_finished_event() const {
     return received_finished_event_;
@@ -227,10 +231,10 @@
   // When pushed from a main-thread controller to a compositor-thread
   // controller, an animation will initially only affect pending observers
   // (corresponding to layers in the pending tree). Animations that only
-  // affect pending observers are able to reach the Starting state and tick
+  // affect pending observers are able to reach the STARTING state and tick
   // pending observers, but cannot proceed any further and do not tick active
   // observers. After activation, such animations affect both kinds of observers
-  // and are able to proceed past the Starting state. When the removal of
+  // and are able to proceed past the STARTING state. When the removal of
   // an animation is pushed from a main-thread controller to a
   // compositor-thread controller, this initially only makes the animation
   // stop affecting pending observers. After activation, such animations no
diff --git a/cc/animation/animation_curve.cc b/cc/animation/animation_curve.cc
index 3acff5d..9e8cae9 100644
--- a/cc/animation/animation_curve.cc
+++ b/cc/animation/animation_curve.cc
@@ -10,48 +10,50 @@
 namespace cc {
 
 const ColorAnimationCurve* AnimationCurve::ToColorAnimationCurve() const {
-  DCHECK(Type() == AnimationCurve::Color);
+  DCHECK(Type() == AnimationCurve::COLOR);
   return static_cast<const ColorAnimationCurve*>(this);
 }
 
-AnimationCurve::CurveType ColorAnimationCurve::Type() const { return Color; }
+AnimationCurve::CurveType ColorAnimationCurve::Type() const {
+  return COLOR;
+}
 
 const FloatAnimationCurve* AnimationCurve::ToFloatAnimationCurve() const {
-  DCHECK(Type() == AnimationCurve::Float);
+  DCHECK(Type() == AnimationCurve::FLOAT);
   return static_cast<const FloatAnimationCurve*>(this);
 }
 
 AnimationCurve::CurveType FloatAnimationCurve::Type() const {
-  return Float;
+  return FLOAT;
 }
 
 const TransformAnimationCurve* AnimationCurve::ToTransformAnimationCurve()
     const {
-  DCHECK(Type() == AnimationCurve::Transform);
+  DCHECK(Type() == AnimationCurve::TRANSFORM);
   return static_cast<const TransformAnimationCurve*>(this);
 }
 
 AnimationCurve::CurveType TransformAnimationCurve::Type() const {
-  return Transform;
+  return TRANSFORM;
 }
 
 const FilterAnimationCurve* AnimationCurve::ToFilterAnimationCurve() const {
-  DCHECK(Type() == AnimationCurve::Filter);
+  DCHECK(Type() == AnimationCurve::FILTER);
   return static_cast<const FilterAnimationCurve*>(this);
 }
 
 AnimationCurve::CurveType FilterAnimationCurve::Type() const {
-  return Filter;
+  return FILTER;
 }
 
 const ScrollOffsetAnimationCurve* AnimationCurve::ToScrollOffsetAnimationCurve()
     const {
-  DCHECK(Type() == AnimationCurve::ScrollOffset);
+  DCHECK(Type() == AnimationCurve::SCROLL_OFFSET);
   return static_cast<const ScrollOffsetAnimationCurve*>(this);
 }
 
 ScrollOffsetAnimationCurve* AnimationCurve::ToScrollOffsetAnimationCurve() {
-  DCHECK(Type() == AnimationCurve::ScrollOffset);
+  DCHECK(Type() == AnimationCurve::SCROLL_OFFSET);
   return static_cast<ScrollOffsetAnimationCurve*>(this);
 }
 
diff --git a/cc/animation/animation_curve.h b/cc/animation/animation_curve.h
index 74ce11b..1074203 100644
--- a/cc/animation/animation_curve.h
+++ b/cc/animation/animation_curve.h
@@ -27,7 +27,7 @@
 // An animation curve is a function that returns a value given a time.
 class CC_EXPORT AnimationCurve {
  public:
-  enum CurveType { Color, Float, Transform, Filter, ScrollOffset };
+  enum CurveType { COLOR, FLOAT, TRANSFORM, FILTER, SCROLL_OFFSET };
 
   virtual ~AnimationCurve() {}
 
diff --git a/cc/animation/animation_events.h b/cc/animation/animation_events.h
index e40ca3f..d268162 100644
--- a/cc/animation/animation_events.h
+++ b/cc/animation/animation_events.h
@@ -15,7 +15,7 @@
 namespace cc {
 
 struct CC_EXPORT AnimationEvent {
-  enum Type { Started, Finished, Aborted, PropertyUpdate };
+  enum Type { STARTED, FINISHED, ABORTED, PROPERTY_UPDATE };
 
   AnimationEvent(Type type,
                  int layer_id,
diff --git a/cc/animation/animation_unittest.cc b/cc/animation/animation_unittest.cc
index 2522f19..9345c6b 100644
--- a/cc/animation/animation_unittest.cc
+++ b/cc/animation/animation_unittest.cc
@@ -23,9 +23,7 @@
                                       double playback_rate) {
   scoped_ptr<Animation> to_return(
       Animation::Create(make_scoped_ptr(new FakeFloatAnimationCurve(duration)),
-                        0,
-                        1,
-                        Animation::Opacity));
+                        0, 1, Animation::OPACITY));
   to_return->set_iterations(iterations);
   to_return->set_playback_rate(playback_rate);
   return to_return.Pass();
@@ -93,7 +91,7 @@
 
 TEST(AnimationTest, TrimTimeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(
       1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0)).InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -110,7 +108,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateInfiniteIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -127,7 +125,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateOneIteration) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -144,7 +142,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(2));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -167,7 +165,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoHalfIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(2.5));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -194,7 +192,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateReverseInfiniteIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -211,7 +209,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateReverseOneIteration) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -228,7 +226,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateReverseTwoIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -267,7 +265,7 @@
 TEST(AnimationTest, TrimTimeStartTimeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_start_time(TicksFromSecondsF(4));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))
@@ -298,7 +296,7 @@
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(4000));
   anim->set_start_time(TicksFromSecondsF(4));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
@@ -326,7 +324,7 @@
 TEST(AnimationTest, TrimTimeNegativeTimeOffsetReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(-4000));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
 
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
@@ -340,15 +338,15 @@
 
 TEST(AnimationTest, TrimTimePauseResume) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
                      .InSecondsF());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.5));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.5));
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                      .InSecondsF());
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(1024.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(1024.0));
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                      .InSecondsF());
   EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5))
@@ -357,16 +355,16 @@
 
 TEST(AnimationTest, TrimTimePauseResumeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::Reverse);
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->set_direction(Animation::DIRECTION_REVERSE);
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
                      .InSecondsF());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.25));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.25));
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                       .InSecondsF());
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(1024.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(1024.0));
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                       .InSecondsF());
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.75))
@@ -375,7 +373,7 @@
 
 TEST(AnimationTest, TrimTimeSuspendResume) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
@@ -392,8 +390,8 @@
 
 TEST(AnimationTest, TrimTimeSuspendResumeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::Reverse);
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->set_direction(Animation::DIRECTION_REVERSE);
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -410,7 +408,7 @@
 
 TEST(AnimationTest, TrimTimeZeroDuration) {
   scoped_ptr<Animation> anim(CreateAnimation(0, 0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                    .InSecondsF());
   EXPECT_EQ(
@@ -421,7 +419,7 @@
 
 TEST(AnimationTest, TrimTimeStarting) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 5.0));
-  anim->SetRunState(Animation::Starting, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::STARTING, TicksFromSecondsF(0.0));
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
@@ -448,7 +446,7 @@
 
 TEST(AnimationTest, TrimTimeNeedsSynchronizedStartTime) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 5.0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   anim->set_needs_synchronized_start_time(true);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
@@ -475,7 +473,7 @@
 
 TEST(AnimationTest, IsFinishedAtZeroIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(1.0)));
@@ -483,7 +481,7 @@
 
 TEST(AnimationTest, IsFinishedAtOneIteration) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(1.0)));
@@ -492,7 +490,7 @@
 
 TEST(AnimationTest, IsFinishedAtInfiniteIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.5)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(1.0)));
@@ -502,7 +500,7 @@
 TEST(AnimationTest, IsFinishedNegativeTimeOffset) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(-500));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
 
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
@@ -516,7 +514,7 @@
 TEST(AnimationTest, IsFinishedPositiveTimeOffset) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(500));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
 
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
@@ -526,58 +524,58 @@
 
 TEST(AnimationTest, IsFinishedAtNotRunning) {
   scoped_ptr<Animation> anim(CreateAnimation(0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::WaitingForTargetAvailability,
+  anim->SetRunState(Animation::WAITING_FOR_TARGET_AVAILABILITY,
                     TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::Aborted, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::ABORTED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
 }
 
 TEST(AnimationTest, IsFinished) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::WaitingForTargetAvailability,
+  anim->SetRunState(Animation::WAITING_FOR_TARGET_AVAILABILITY,
                     TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
-  anim->SetRunState(Animation::Aborted, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::ABORTED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
 }
 
 TEST(AnimationTest, IsFinishedNeedsSynchronizedStartTime) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(2.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(2.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(2.0));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(2.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::WaitingForTargetAvailability,
+  anim->SetRunState(Animation::WAITING_FOR_TARGET_AVAILABILITY,
                     TicksFromSecondsF(2.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
-  anim->SetRunState(Animation::Aborted, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::ABORTED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
 }
 
 TEST(AnimationTest, RunStateChangesIgnoredWhileSuspended) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->Suspend(TicksFromSecondsF(0));
-  EXPECT_EQ(Animation::Paused, anim->run_state());
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
-  EXPECT_EQ(Animation::Paused, anim->run_state());
+  EXPECT_EQ(Animation::PAUSED, anim->run_state());
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
+  EXPECT_EQ(Animation::PAUSED, anim->run_state());
   anim->Resume(TicksFromSecondsF(0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
-  EXPECT_EQ(Animation::Running, anim->run_state());
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
+  EXPECT_EQ(Animation::RUNNING, anim->run_state());
 }
 
 TEST(AnimationTest, TrimTimePlaybackNormal) {
@@ -708,7 +706,7 @@
 
 TEST(AnimationTest, TrimTimePlaybackNormalDoubleReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 1, -1));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                    .InSecondsF());
   EXPECT_EQ(
@@ -723,7 +721,7 @@
 
 TEST(AnimationTest, TrimTimePlaybackFastDoubleReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 4, -2));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                    .InSecondsF());
   EXPECT_EQ(
@@ -742,7 +740,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFast) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, 2));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -767,7 +765,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFastReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, 2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
   EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
@@ -794,7 +792,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFastDoubleReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, -2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -820,7 +818,7 @@
 TEST(AnimationTest,
      TrimTimeAlternateReverseThreeIterationsPlaybackFastAlternateReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(3, 2, -2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -854,7 +852,7 @@
 TEST(AnimationTest,
      TrimTimeAlternateReverseTwoIterationsPlaybackNormalAlternate) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, -1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
@@ -898,7 +896,7 @@
 
 TEST(AnimationTest, TrimTimeIterationStartAlternate) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 1, 1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   anim->set_iteration_start(0.3);
   EXPECT_EQ(0.3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
@@ -918,7 +916,7 @@
 
 TEST(AnimationTest, TrimTimeIterationStartAlternateThreeIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(3, 1, 1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   anim->set_iteration_start(1);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
@@ -943,7 +941,7 @@
 TEST(AnimationTest,
      TrimTimeIterationStartAlternateThreeIterationsPlaybackReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(3, 1, -1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   anim->set_iteration_start(1);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
@@ -959,22 +957,22 @@
 
 TEST(AnimationTest, InEffectFillMode) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_fill_mode(Animation::FillModeNone);
+  anim->set_fill_mode(Animation::FILL_MODE_NONE);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeForwards);
+  anim->set_fill_mode(Animation::FILL_MODE_FORWARDS);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBackwards);
+  anim->set_fill_mode(Animation::FILL_MODE_BACKWARDS);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBoth);
+  anim->set_fill_mode(Animation::FILL_MODE_BOTH);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
@@ -982,22 +980,22 @@
 
 TEST(AnimationTest, InEffectFillModePlayback) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 1, -1));
-  anim->set_fill_mode(Animation::FillModeNone);
+  anim->set_fill_mode(Animation::FILL_MODE_NONE);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeForwards);
+  anim->set_fill_mode(Animation::FILL_MODE_FORWARDS);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBackwards);
+  anim->set_fill_mode(Animation::FILL_MODE_BACKWARDS);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBoth);
+  anim->set_fill_mode(Animation::FILL_MODE_BOTH);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index 1892dd4..c253dae 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -45,7 +45,7 @@
                                               base::TimeDelta time_offset) {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->id() == animation_id) {
-      animations_[i]->SetRunState(Animation::Paused,
+      animations_[i]->SetRunState(Animation::PAUSED,
                                   time_offset + animations_[i]->start_time());
     }
   }
@@ -65,13 +65,13 @@
   auto animations_to_remove =
       animations_.remove_if(HasAnimationId(animation_id));
   for (auto it = animations_to_remove; it != animations_.end(); ++it) {
-    if ((*it)->target_property() == Animation::ScrollOffset) {
+    if ((*it)->target_property() == Animation::SCROLL_OFFSET) {
       scroll_offset_animation_was_interrupted_ = true;
       break;
     }
   }
   animations_.erase(animations_to_remove, animations_.end());
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 struct HasAnimationIdAndProperty {
@@ -92,12 +92,12 @@
     Animation::TargetProperty target_property) {
   auto animations_to_remove = animations_.remove_if(
       HasAnimationIdAndProperty(animation_id, target_property));
-  if (target_property == Animation::ScrollOffset &&
+  if (target_property == Animation::SCROLL_OFFSET &&
       animations_to_remove != animations_.end())
     scroll_offset_animation_was_interrupted_ = true;
 
   animations_.erase(animations_to_remove, animations_.end());
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 void LayerAnimationController::AbortAnimations(
@@ -105,7 +105,7 @@
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->target_property() == target_property &&
         !animations_[i]->is_finished())
-      animations_[i]->SetRunState(Animation::Aborted, last_tick_time_);
+      animations_[i]->SetRunState(Animation::ABORTED, last_tick_time_);
   }
 }
 
@@ -125,8 +125,8 @@
   RemoveAnimationsCompletedOnMainThread(controller_impl);
 
   PushPropertiesToImplThread(controller_impl);
-  controller_impl->UpdateActivation(NormalActivation);
-  UpdateActivation(NormalActivation);
+  controller_impl->UpdateActivation(NORMAL_ACTIVATION);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 void LayerAnimationController::Animate(base::TimeTicks monotonic_time) {
@@ -157,11 +157,9 @@
     base::TimeDelta trimmed =
         animation->TrimTimeToCurrentIteration(monotonic_time);
     switch (animation->target_property()) {
-      case Animation::Opacity: {
-        AnimationEvent event(AnimationEvent::PropertyUpdate,
-                             id_,
-                             animation->group(),
-                             Animation::Opacity,
+      case Animation::OPACITY: {
+        AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
+                             animation->group(), Animation::OPACITY,
                              monotonic_time);
         const FloatAnimationCurve* float_animation_curve =
             animation->curve()->ToFloatAnimationCurve();
@@ -171,11 +169,9 @@
         break;
       }
 
-      case Animation::Transform: {
-        AnimationEvent event(AnimationEvent::PropertyUpdate,
-                             id_,
-                             animation->group(),
-                             Animation::Transform,
+      case Animation::TRANSFORM: {
+        AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
+                             animation->group(), Animation::TRANSFORM,
                              monotonic_time);
         const TransformAnimationCurve* transform_animation_curve =
             animation->curve()->ToTransformAnimationCurve();
@@ -185,11 +181,9 @@
         break;
       }
 
-      case Animation::Filter: {
-        AnimationEvent event(AnimationEvent::PropertyUpdate,
-                             id_,
-                             animation->group(),
-                             Animation::Filter,
+      case Animation::FILTER: {
+        AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
+                             animation->group(), Animation::FILTER,
                              monotonic_time);
         const FilterAnimationCurve* filter_animation_curve =
             animation->curve()->ToFilterAnimationCurve();
@@ -199,17 +193,16 @@
         break;
       }
 
-      case Animation::BackgroundColor: { break; }
-
-      case Animation::ScrollOffset: {
-        // Impl-side changes to scroll offset are already sent back to the
-        // main thread (e.g. for user-driven scrolling), so a PropertyUpdate
-        // isn't needed.
+      case Animation::BACKGROUND_COLOR: {
         break;
       }
 
-      case Animation::TargetPropertyEnumSize:
-        NOTREACHED();
+      case Animation::SCROLL_OFFSET: {
+        // Impl-side changes to scroll offset are already sent back to the
+        // main thread (e.g. for user-driven scrolling), so a PROPERTY_UPDATE
+        // isn't needed.
+        break;
+      }
     }
   }
 }
@@ -237,7 +230,7 @@
 
   AccumulatePropertyUpdates(last_tick_time_, events);
 
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 struct AffectsNoObservers {
@@ -258,13 +251,13 @@
                                   AffectsNoObservers()),
                     animations_.end());
   scroll_offset_animation_was_interrupted_ = false;
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 void LayerAnimationController::AddAnimation(scoped_ptr<Animation> animation) {
   animations_.push_back(animation.Pass());
   needs_to_start_animations_ = true;
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 Animation* LayerAnimationController::GetAnimation(
@@ -315,7 +308,7 @@
   if (registrar_)
     registrar_->RegisterAnimationController(this);
 
-  UpdateActivation(ForceActivation);
+  UpdateActivation(FORCE_ACTIVATION);
 }
 
 void LayerAnimationController::NotifyAnimationStarted(
@@ -375,7 +368,7 @@
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->group() == event.group_id &&
         animations_[i]->target_property() == event.target_property) {
-      animations_[i]->SetRunState(Animation::Aborted, event.monotonic_time);
+      animations_[i]->SetRunState(Animation::ABORTED, event.monotonic_time);
     }
   }
 }
@@ -385,11 +378,11 @@
   bool notify_active_observers = true;
   bool notify_pending_observers = true;
   switch (event.target_property) {
-    case Animation::Opacity:
+    case Animation::OPACITY:
       NotifyObserversOpacityAnimated(
           event.opacity, notify_active_observers, notify_pending_observers);
       break;
-    case Animation::Transform:
+    case Animation::TRANSFORM:
       NotifyObserversTransformAnimated(
           event.transform, notify_active_observers, notify_pending_observers);
       break;
@@ -423,7 +416,7 @@
 bool LayerAnimationController::HasFilterAnimationThatInflatesBounds() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (!animations_[i]->is_finished() &&
-        animations_[i]->target_property() == Animation::Filter &&
+        animations_[i]->target_property() == Animation::FILTER &&
         animations_[i]
             ->curve()
             ->ToFilterAnimationCurve()
@@ -435,7 +428,7 @@
 }
 
 bool LayerAnimationController::HasTransformAnimationThatInflatesBounds() const {
-  return IsAnimatingProperty(Animation::Transform);
+  return IsAnimatingProperty(Animation::TRANSFORM);
 }
 
 bool LayerAnimationController::FilterAnimationBoundsForBox(
@@ -459,7 +452,7 @@
   *bounds = gfx::BoxF();
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -478,7 +471,7 @@
 bool LayerAnimationController::HasAnimationThatAffectsScale() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -493,7 +486,7 @@
 bool LayerAnimationController::HasOnlyTranslationTransforms() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -508,7 +501,7 @@
 bool LayerAnimationController::AnimationsPreserveAxisAlignment() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -524,17 +517,17 @@
   *max_scale = 0.f;
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     bool forward_direction = true;
     switch (animations_[i]->direction()) {
-      case Animation::Normal:
-      case Animation::Alternate:
+      case Animation::DIRECTION_NORMAL:
+      case Animation::DIRECTION_ALTERNATE:
         forward_direction = animations_[i]->playback_rate() >= 0.0;
         break;
-      case Animation::Reverse:
-      case Animation::AlternateReverse:
+      case Animation::DIRECTION_REVERSE:
+      case Animation::DIRECTION_ALTERNATE_REVERSE:
         forward_direction = animations_[i]->playback_rate() < 0.0;
         break;
     }
@@ -571,7 +564,7 @@
       continue;
 
     // Scroll animations always start at the current scroll offset.
-    if (animations_[i]->target_property() == Animation::ScrollOffset) {
+    if (animations_[i]->target_property() == Animation::SCROLL_OFFSET) {
       gfx::ScrollOffset current_scroll_offset;
       if (controller_impl->value_provider_) {
         current_scroll_offset =
@@ -587,7 +580,7 @@
 
     // The new animation should be set to run as soon as possible.
     Animation::RunState initial_run_state =
-        Animation::WaitingForTargetAvailability;
+        Animation::WAITING_FOR_TARGET_AVAILABILITY;
     scoped_ptr<Animation> to_add(
         animations_[i]->CloneAndInitialize(initial_run_state));
     DCHECK(!to_add->needs_synchronized_start_time());
@@ -600,14 +593,14 @@
     Animation* animation,
     const LayerAnimationController* main_thread_controller) {
   if (animation->is_impl_only()) {
-    return (animation->run_state() == Animation::WaitingForDeletion);
+    return (animation->run_state() == Animation::WAITING_FOR_DELETION);
   } else {
     return !main_thread_controller->GetAnimationById(animation->id());
   }
 }
 
 static bool AffectsActiveOnlyAndIsWaitingForDeletion(Animation* animation) {
-  return animation->run_state() == Animation::WaitingForDeletion &&
+  return animation->run_state() == Animation::WAITING_FOR_DELETION &&
          !animation->affects_pending_observers();
 }
 
@@ -615,7 +608,7 @@
     LayerAnimationController* controller_impl) const {
   // Animations removed on the main thread should no longer affect pending
   // observers, and should stop affecting active observers after the next call
-  // to ActivateAnimations. If already WaitingForDeletion, they can be removed
+  // to ActivateAnimations. If already WAITING_FOR_DELETION, they can be removed
   // immediately.
   ScopedPtrVector<Animation>& animations = controller_impl->animations_;
   for (size_t i = 0; i < animations.size(); ++i) {
@@ -652,8 +645,8 @@
 
   animations_waiting_for_target.reserve(animations_.size());
   for (size_t i = 0; i < animations_.size(); ++i) {
-    if (animations_[i]->run_state() == Animation::Starting ||
-        animations_[i]->run_state() == Animation::Running) {
+    if (animations_[i]->run_state() == Animation::STARTING ||
+        animations_[i]->run_state() == Animation::RUNNING) {
       if (animations_[i]->affects_active_observers()) {
         blocked_properties_for_active_observers.insert(
             animations_[i]->target_property());
@@ -663,7 +656,7 @@
             animations_[i]->target_property());
       }
     } else if (animations_[i]->run_state() ==
-               Animation::WaitingForTargetAvailability) {
+               Animation::WAITING_FOR_TARGET_AVAILABILITY) {
       animations_waiting_for_target.push_back(i);
     }
   }
@@ -677,7 +670,7 @@
     // for target because it might have changed the run state while handling
     // previous animation in this loop (if they belong to same group).
     if (animation_waiting_for_target->run_state() ==
-        Animation::WaitingForTargetAvailability) {
+        Animation::WAITING_FOR_TARGET_AVAILABILITY) {
       TargetProperties enqueued_properties;
       bool affects_active_observers =
           animation_waiting_for_target->affects_active_observers();
@@ -715,12 +708,12 @@
       // If the intersection is null, then we are free to start the animations
       // in the group.
       if (null_intersection) {
-        animation_waiting_for_target->SetRunState(Animation::Starting,
+        animation_waiting_for_target->SetRunState(Animation::STARTING,
                                                   monotonic_time);
         for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
           if (animation_waiting_for_target->group() ==
               animations_[j]->group()) {
-            animations_[j]->SetRunState(Animation::Starting, monotonic_time);
+            animations_[j]->SetRunState(Animation::STARTING, monotonic_time);
           }
         }
       } else {
@@ -734,18 +727,16 @@
     base::TimeTicks monotonic_time,
     AnimationEventsVector* events) {
   for (size_t i = 0; i < animations_.size(); ++i) {
-    if (animations_[i]->run_state() == Animation::Starting &&
+    if (animations_[i]->run_state() == Animation::STARTING &&
         animations_[i]->affects_active_observers()) {
-      animations_[i]->SetRunState(Animation::Running, monotonic_time);
+      animations_[i]->SetRunState(Animation::RUNNING, monotonic_time);
       if (!animations_[i]->has_set_start_time() &&
           !animations_[i]->needs_synchronized_start_time())
         animations_[i]->set_start_time(monotonic_time);
       if (events) {
-        AnimationEvent started_event(AnimationEvent::Started,
-                                     id_,
-                                     animations_[i]->group(),
-                                     animations_[i]->target_property(),
-                                     monotonic_time);
+        AnimationEvent started_event(
+            AnimationEvent::STARTED, id_, animations_[i]->group(),
+            animations_[i]->target_property(), monotonic_time);
         started_event.is_impl_only = animations_[i]->is_impl_only();
         if (started_event.is_impl_only)
           NotifyAnimationStarted(started_event);
@@ -760,9 +751,9 @@
     base::TimeTicks monotonic_time) {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->IsFinishedAt(monotonic_time) &&
-        animations_[i]->run_state() != Animation::Aborted &&
-        animations_[i]->run_state() != Animation::WaitingForDeletion)
-      animations_[i]->SetRunState(Animation::Finished, monotonic_time);
+        animations_[i]->run_state() != Animation::ABORTED &&
+        animations_[i]->run_state() != Animation::WAITING_FOR_DELETION)
+      animations_[i]->SetRunState(Animation::FINISHED, monotonic_time);
   }
 }
 
@@ -774,21 +765,19 @@
 
   animations_with_same_group_id.reserve(animations_.size());
   // Non-aborted animations are marked for deletion after a corresponding
-  // AnimationEvent::Finished event is sent or received. This means that if
+  // AnimationEvent::FINISHED event is sent or received. This means that if
   // we don't have an events vector, we must ensure that non-aborted animations
   // have received a finished event before marking them for deletion.
   for (size_t i = 0; i < animations_.size(); i++) {
     int group_id = animations_[i]->group();
-    if (animations_[i]->run_state() == Animation::Aborted) {
+    if (animations_[i]->run_state() == Animation::ABORTED) {
       if (events && !animations_[i]->is_impl_only()) {
-        AnimationEvent aborted_event(AnimationEvent::Aborted,
-                                     id_,
-                                     group_id,
+        AnimationEvent aborted_event(AnimationEvent::ABORTED, id_, group_id,
                                      animations_[i]->target_property(),
                                      monotonic_time);
         events->push_back(aborted_event);
       }
-      animations_[i]->SetRunState(Animation::WaitingForDeletion,
+      animations_[i]->SetRunState(Animation::WAITING_FOR_DELETION,
                                   monotonic_time);
       marked_animations_for_deletions = true;
       continue;
@@ -797,13 +786,13 @@
     bool all_anims_with_same_id_are_finished = false;
 
     // Since deleting an animation on the main thread leads to its deletion
-    // on the impl thread, we only mark a Finished main thread animation for
-    // deletion once it has received a Finished event from the impl thread.
+    // on the impl thread, we only mark a FINISHED main thread animation for
+    // deletion once it has received a FINISHED event from the impl thread.
     bool animation_i_will_send_or_has_received_finish_event =
         events || animations_[i]->received_finished_event();
     // If an animation is finished, and not already marked for deletion,
     // find out if all other animations in the same group are also finished.
-    if (animations_[i]->run_state() == Animation::Finished &&
+    if (animations_[i]->run_state() == Animation::FINISHED &&
         animation_i_will_send_or_has_received_finish_event) {
       // Clear the animations_with_same_group_id if it was added for
       // the previous animation's iteration.
@@ -815,16 +804,16 @@
             events || animations_[j]->received_finished_event();
         if (group_id == animations_[j]->group()) {
           if (!animations_[j]->is_finished() ||
-              (animations_[j]->run_state() == Animation::Finished &&
+              (animations_[j]->run_state() == Animation::FINISHED &&
                !animation_j_will_send_or_has_received_finish_event)) {
             all_anims_with_same_id_are_finished = false;
             break;
           } else if (j >= i &&
-                     animations_[j]->run_state() != Animation::Aborted) {
+                     animations_[j]->run_state() != Animation::ABORTED) {
             // Mark down the animations which belong to the same group
             // and is not yet aborted. If this current iteration finds that all
             // animations with same ID are finished, then the marked
-            // animations below will be set to WaitingForDeletion in next
+            // animations below will be set to WAITING_FOR_DELETION in next
             // iteration.
             animations_with_same_group_id.push_back(j);
           }
@@ -839,8 +828,7 @@
         size_t animation_index = animations_with_same_group_id[j];
           if (events) {
             AnimationEvent finished_event(
-                AnimationEvent::Finished,
-                id_,
+                AnimationEvent::FINISHED, id_,
                 animations_[animation_index]->group(),
                 animations_[animation_index]->target_property(),
                 monotonic_time);
@@ -852,7 +840,7 @@
               events->push_back(finished_event);
           }
           animations_[animation_index]->SetRunState(
-              Animation::WaitingForDeletion, monotonic_time);
+              Animation::WAITING_FOR_DELETION, monotonic_time);
       }
       marked_animations_for_deletions = true;
     }
@@ -862,7 +850,7 @@
 }
 
 static bool IsWaitingForDeletion(Animation* animation) {
-  return animation->run_state() == Animation::WaitingForDeletion;
+  return animation->run_state() == Animation::WAITING_FOR_DELETION;
 }
 
 void LayerAnimationController::PurgeAnimationsMarkedForDeletion() {
@@ -875,9 +863,9 @@
 
 void LayerAnimationController::TickAnimations(base::TimeTicks monotonic_time) {
   for (size_t i = 0; i < animations_.size(); ++i) {
-    if (animations_[i]->run_state() == Animation::Starting ||
-        animations_[i]->run_state() == Animation::Running ||
-        animations_[i]->run_state() == Animation::Paused) {
+    if (animations_[i]->run_state() == Animation::STARTING ||
+        animations_[i]->run_state() == Animation::RUNNING ||
+        animations_[i]->run_state() == Animation::PAUSED) {
       if (!animations_[i]->InEffect(monotonic_time))
         continue;
 
@@ -885,7 +873,7 @@
           animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
 
       switch (animations_[i]->target_property()) {
-        case Animation::Transform: {
+        case Animation::TRANSFORM: {
           const TransformAnimationCurve* transform_animation_curve =
               animations_[i]->curve()->ToTransformAnimationCurve();
           const gfx::Transform transform =
@@ -897,7 +885,7 @@
           break;
         }
 
-        case Animation::Opacity: {
+        case Animation::OPACITY: {
           const FloatAnimationCurve* float_animation_curve =
               animations_[i]->curve()->ToFloatAnimationCurve();
           const float opacity = std::max(
@@ -909,7 +897,7 @@
           break;
         }
 
-        case Animation::Filter: {
+        case Animation::FILTER: {
           const FilterAnimationCurve* filter_animation_curve =
               animations_[i]->curve()->ToFilterAnimationCurve();
           const FilterOperations filter =
@@ -921,12 +909,12 @@
           break;
         }
 
-        case Animation::BackgroundColor: {
+        case Animation::BACKGROUND_COLOR: {
           // Not yet implemented.
           break;
         }
 
-        case Animation::ScrollOffset: {
+        case Animation::SCROLL_OFFSET: {
           const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
               animations_[i]->curve()->ToScrollOffsetAnimationCurve();
           const gfx::ScrollOffset scroll_offset =
@@ -937,22 +925,18 @@
               animations_[i]->affects_pending_observers());
           break;
         }
-
-        // Do nothing for sentinel value.
-        case Animation::TargetPropertyEnumSize:
-          NOTREACHED();
       }
     }
   }
 }
 
 void LayerAnimationController::UpdateActivation(UpdateActivationType type) {
-  bool force = type == ForceActivation;
+  bool force = type == FORCE_ACTIVATION;
   if (registrar_) {
     bool was_active = is_active_;
     is_active_ = false;
     for (size_t i = 0; i < animations_.size(); ++i) {
-      if (animations_[i]->run_state() != Animation::WaitingForDeletion) {
+      if (animations_[i]->run_state() != Animation::WAITING_FOR_DELETION) {
         is_active_ = true;
         break;
       }
diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h
index 48c3bdc..d459515 100644
--- a/cc/animation/layer_animation_controller.h
+++ b/cc/animation/layer_animation_controller.h
@@ -179,10 +179,7 @@
 
   void TickAnimations(base::TimeTicks monotonic_time);
 
-  enum UpdateActivationType {
-    NormalActivation,
-    ForceActivation
-  };
+  enum UpdateActivationType { NORMAL_ACTIVATION, FORCE_ACTIVATION };
   void UpdateActivation(UpdateActivationType type);
 
   void NotifyObserversOpacityAnimated(float opacity,
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc
index ba639b3..d8d0b05 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -49,7 +49,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   EXPECT_FALSE(controller->needs_to_start_animations_for_testing());
   EXPECT_FALSE(controller_impl->needs_to_start_animations_for_testing());
@@ -63,7 +63,7 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 }
 
@@ -79,7 +79,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   int animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
@@ -88,7 +88,7 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   AnimationEventsVector events;
@@ -122,13 +122,13 @@
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
 
   const TimeTicks start_time = TicksFromSecondsF(123);
-  controller->GetAnimation(Animation::Opacity)->set_start_time(start_time);
+  controller->GetAnimation(Animation::OPACITY)->set_start_time(start_time);
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   AnimationEventsVector events;
@@ -202,8 +202,8 @@
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, nullptr);
-  EXPECT_EQ(Animation::Finished,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::FINISHED,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(1u, registrar->active_animation_controllers().size());
 
   events.reset(new AnimationEventsVector);
@@ -211,8 +211,8 @@
                            TimeDelta::FromMilliseconds(1500));
   controller_impl->UpdateState(true, events.get());
 
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller_impl->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller_impl->GetAnimation(Animation::OPACITY)->run_state());
   // The impl thread controller should have de-activated.
   EXPECT_EQ(0u, registrar_impl->active_animation_controllers().size());
 
@@ -221,8 +221,8 @@
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
   controller->UpdateState(true, nullptr);
 
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   // The main thread controller should have de-activated.
   EXPECT_EQ(0u, registrar->active_animation_controllers().size());
 
@@ -247,7 +247,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   int animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
@@ -256,7 +256,7 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   // Start the animations on each controller.
@@ -265,22 +265,22 @@
   controller_impl->UpdateState(true, &events);
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller->GetAnimationById(animation_id)->run_state());
 
   // Pause the main-thread animation.
   controller->PauseAnimation(
       animation_id,
       TimeDelta::FromMilliseconds(1000) + TimeDelta::FromMilliseconds(1000));
-  EXPECT_EQ(Animation::Paused,
+  EXPECT_EQ(Animation::PAUSED,
             controller->GetAnimationById(animation_id)->run_state());
 
   // The pause run state change should make it to the impl thread controller.
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
-  EXPECT_EQ(Animation::Paused,
+  EXPECT_EQ(Animation::PAUSED,
             controller_impl->GetAnimationById(animation_id)->run_state());
 }
 
@@ -294,7 +294,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   int animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
@@ -304,15 +304,12 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   // Notify main thread controller that the animation has started.
-  AnimationEvent animation_started_event(AnimationEvent::Started,
-                                         0,
-                                         group_id,
-                                         Animation::Opacity,
-                                         kInitialTickTime);
+  AnimationEvent animation_started_event(AnimationEvent::STARTED, 0, group_id,
+                                         Animation::OPACITY, kInitialTickTime);
   controller->NotifyAnimationStarted(animation_started_event);
 
   // Force animation to complete on impl thread.
@@ -351,9 +348,9 @@
   controller_impl->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   controller_impl->UpdateState(true, events.get());
 
-  // There should be a Started event for the animation.
+  // There should be a STARTED event for the animation.
   EXPECT_EQ(1u, events->size());
-  EXPECT_EQ(AnimationEvent::Started, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[0].type);
   controller->NotifyAnimationStarted((*events)[0]);
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
@@ -369,13 +366,13 @@
 
   EXPECT_TRUE(dummy_impl.animation_waiting_for_deletion());
 
-  // There should be a Finished event for the animation.
+  // There should be a FINISHED event for the animation.
   EXPECT_EQ(1u, events->size());
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[0].type);
 
   // Neither controller should have deleted the animation yet.
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  EXPECT_TRUE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  EXPECT_TRUE(controller_impl->GetAnimation(Animation::OPACITY));
 
   controller->NotifyAnimationFinished((*events)[0]);
 
@@ -399,7 +396,7 @@
     const AnimationEventsVector* events) {
   const AnimationEvent* event = 0;
   for (size_t i = 0; i < events->size(); ++i)
-    if ((*events)[i].type == AnimationEvent::PropertyUpdate)
+    if ((*events)[i].type == AnimationEvent::PROPERTY_UPDATE)
       event = &(*events)[i];
 
   return event;
@@ -415,8 +412,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   EXPECT_FALSE(controller->needs_to_start_animations_for_testing());
   controller->AddAnimation(to_add.Pass());
@@ -447,8 +443,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_is_impl_only(true);
 
   controller_impl->AddAnimation(to_add.Pass());
@@ -488,7 +483,7 @@
   scoped_ptr<KeyframedTransformAnimationCurve> curve(
       KeyframedTransformAnimationCurve::Create());
 
-  // Create simple Transform animation.
+  // Create simple TRANSFORM animation.
   TransformOperations operations;
   curve->AddKeyframe(
       TransformKeyframe::Create(base::TimeDelta(), operations, nullptr));
@@ -497,7 +492,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::Transform));
+      Animation::Create(curve.Pass(), 1, 0, Animation::TRANSFORM));
   animation->set_is_impl_only(true);
   controller_impl->AddAnimation(animation.Pass());
 
@@ -549,7 +544,7 @@
                                             end_filters, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::Filter));
+      Animation::Create(curve.Pass(), 1, 0, Animation::FILTER));
   controller->AddAnimation(animation.Pass());
 
   controller->Animate(kInitialTickTime);
@@ -587,7 +582,7 @@
   scoped_ptr<KeyframedFilterAnimationCurve> curve(
       KeyframedFilterAnimationCurve::Create());
 
-  // Create simple Filter animation.
+  // Create simple FILTER animation.
   FilterOperations start_filters;
   start_filters.Append(FilterOperation::CreateBrightnessFilter(1.f));
   curve->AddKeyframe(
@@ -598,7 +593,7 @@
                                             end_filters, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::Filter));
+      Animation::Create(curve.Pass(), 1, 0, Animation::FILTER));
   animation->set_is_impl_only(true);
   controller_impl->AddAnimation(animation.Pass());
 
@@ -651,20 +646,20 @@
           EaseInOutTimingFunction::Create().Pass()));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+      Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
 
   dummy_provider_impl.set_scroll_offset(initial_value);
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
-  EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset));
-  TimeDelta duration = controller_impl->GetAnimation(Animation::ScrollOffset)
+  EXPECT_TRUE(controller_impl->GetAnimation(Animation::SCROLL_OFFSET));
+  TimeDelta duration = controller_impl->GetAnimation(Animation::SCROLL_OFFSET)
                            ->curve()
                            ->Duration();
   EXPECT_EQ(
       duration,
-      controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration());
+      controller->GetAnimation(Animation::SCROLL_OFFSET)->curve()->Duration());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
@@ -730,20 +725,20 @@
           EaseInOutTimingFunction::Create().Pass()));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+      Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
 
   dummy_provider.set_scroll_offset(initial_value);
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
-  EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset));
-  TimeDelta duration = controller_impl->GetAnimation(Animation::ScrollOffset)
+  EXPECT_TRUE(controller_impl->GetAnimation(Animation::SCROLL_OFFSET));
+  TimeDelta duration = controller_impl->GetAnimation(Animation::SCROLL_OFFSET)
                            ->curve()
                            ->Duration();
   EXPECT_EQ(
       duration,
-      controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration());
+      controller->GetAnimation(Animation::SCROLL_OFFSET)->curve()->Duration());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
@@ -803,7 +798,7 @@
   double duration_in_seconds = curve->Duration().InSecondsF();
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+      Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
   animation->set_is_impl_only(true);
   controller_impl->AddAnimation(animation.Pass());
 
@@ -857,7 +852,7 @@
 
   int animation_id = 1;
   scoped_ptr<Animation> animation(Animation::Create(
-      curve.Pass(), animation_id, 0, Animation::ScrollOffset));
+      curve.Pass(), animation_id, 0, Animation::SCROLL_OFFSET));
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
   controller->PushAnimationUpdatesTo(controller_impl.get());
@@ -878,8 +873,8 @@
   // Now, test the 2-argument version of RemoveAnimation.
   curve = ScrollOffsetAnimationCurve::Create(
       target_value, EaseInOutTimingFunction::Create().Pass());
-  animation =
-      Animation::Create(curve.Pass(), animation_id, 0, Animation::ScrollOffset);
+  animation = Animation::Create(curve.Pass(), animation_id, 0,
+                                Animation::SCROLL_OFFSET);
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
   controller->PushAnimationUpdatesTo(controller_impl.get());
@@ -975,8 +970,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_is_impl_only(true);
   controller_impl->AddAnimation(to_add.Pass());
 
@@ -1011,8 +1005,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_needs_synchronized_start_time(true);
 
   // We should pause at the first keyframe indefinitely waiting for that
@@ -1033,10 +1026,7 @@
 
   // Send the synchronized start time.
   controller->NotifyAnimationStarted(
-      AnimationEvent(AnimationEvent::Started,
-                     0,
-                     1,
-                     Animation::Opacity,
+      AnimationEvent(AnimationEvent::STARTED, 0, 1, Animation::OPACITY,
                      kInitialTickTime + TimeDelta::FromMilliseconds(2000)));
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(5000));
   controller->UpdateState(true, events.get());
@@ -1057,13 +1047,11 @@
 
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(
-          new FakeFloatTransition(1.0, 1.f, 0.5f)).Pass(),
-      2,
-      Animation::Opacity));
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f))
+          .Pass(),
+      2, Animation::OPACITY));
 
   EXPECT_TRUE(controller->needs_to_start_animations_for_testing());
 
@@ -1099,19 +1087,17 @@
   controller->AddValueObserver(&dummy);
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.f, dummy.opacity());
 
   scoped_ptr<Animation> to_add(CreateAnimation(
-      scoped_ptr<AnimationCurve>(
-          new FakeFloatTransition(1.0, 1.f, 0.5f)).Pass(),
-      2,
-      Animation::Opacity));
-  controller->AbortAnimations(Animation::Opacity);
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f))
+          .Pass(),
+      2, Animation::OPACITY));
+  controller->AbortAnimations(Animation::OPACITY);
   controller->AddAnimation(to_add.Pass());
 
   // Since the previous animation was aborted, the new animation should start
@@ -1137,17 +1123,14 @@
   controller->AddValueObserver(&dummy);
 
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1,
+      Animation::TRANSFORM));
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(),
-      2,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 2,
+      Animation::TRANSFORM));
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2,
-      Animation::Opacity));
+      2, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
@@ -1177,18 +1160,15 @@
   controller->AddValueObserver(&dummy);
 
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(2)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(2)).Pass(), 1,
+      Animation::TRANSFORM));
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(
-          new FakeFloatTransition(1.0, 1.f, 0.5f)).Pass(),
-      2,
-      Animation::Opacity));
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f))
+          .Pass(),
+      2, Animation::OPACITY));
 
   // Animations with id 1 should both start now.
   controller->Animate(kInitialTickTime);
@@ -1223,8 +1203,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_iterations(3);
   controller->AddAnimation(to_add.Pass());
 
@@ -1270,7 +1249,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1, Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_iterations(-1);
   controller->AddAnimation(to_add.Pass());
 
@@ -1298,9 +1277,9 @@
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.75f, dummy.opacity());
 
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  controller->GetAnimation(Animation::Opacity)
-      ->SetRunState(Animation::Aborted,
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  controller->GetAnimation(Animation::OPACITY)
+      ->SetRunState(Animation::ABORTED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(750));
   EXPECT_FALSE(controller->HasActiveAnimation());
   EXPECT_EQ(0.75f, dummy.opacity());
@@ -1317,7 +1296,7 @@
 
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1, Animation::Opacity));
+      1, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
@@ -1328,9 +1307,9 @@
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.5f, dummy.opacity());
 
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  controller->GetAnimation(Animation::Opacity)
-      ->SetRunState(Animation::Paused,
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  controller->GetAnimation(Animation::OPACITY)
+      ->SetRunState(Animation::PAUSED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(500));
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
@@ -1338,9 +1317,9 @@
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.5f, dummy.opacity());
 
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  controller->GetAnimation(Animation::Opacity)
-      ->SetRunState(Animation::Running,
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  controller->GetAnimation(Animation::OPACITY)
+      ->SetRunState(Animation::RUNNING,
                     kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024250));
   controller->UpdateState(true, events.get());
@@ -1364,14 +1343,14 @@
   const int animation_id = 2;
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1, 1,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)).Pass(),
-      animation_id, 1, Animation::Opacity));
+      animation_id, 1, Animation::OPACITY));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.75f))
           .Pass(),
-      3, 2, Animation::Opacity));
+      3, 2, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
@@ -1384,7 +1363,7 @@
 
   EXPECT_TRUE(controller->GetAnimationById(animation_id));
   controller->GetAnimationById(animation_id)
-      ->SetRunState(Animation::Aborted,
+      ->SetRunState(Animation::ABORTED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, events.get());
@@ -1410,24 +1389,23 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)).Pass(),
-      0,
-      Animation::Opacity));
+      0, Animation::OPACITY));
   to_add->set_needs_synchronized_start_time(true);
   controller->AddAnimation(to_add.Pass());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
   EXPECT_TRUE(controller->HasActiveAnimation());
-  Animation* active_animation = controller->GetAnimation(Animation::Opacity);
+  Animation* active_animation = controller->GetAnimation(Animation::OPACITY);
   EXPECT_TRUE(active_animation);
   EXPECT_TRUE(active_animation->needs_synchronized_start_time());
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
 
-  active_animation = controller_impl->GetAnimation(Animation::Opacity);
+  active_animation = controller_impl->GetAnimation(Animation::OPACITY);
   EXPECT_TRUE(active_animation);
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             active_animation->run_state());
 }
 
@@ -1441,17 +1419,15 @@
   controller->AddValueObserver(&dummy);
 
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1,
+      Animation::TRANSFORM));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
 
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2,
-      Animation::Opacity));
+      2, Animation::OPACITY));
 
   // Animate but don't UpdateState.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
@@ -1460,7 +1436,7 @@
   events.reset(new AnimationEventsVector);
   controller->UpdateState(true, events.get());
 
-  // Should have one Started event and one Finished event.
+  // Should have one STARTED event and one FINISHED event.
   EXPECT_EQ(2u, events->size());
   EXPECT_NE((*events)[0].type, (*events)[1].type);
 
@@ -1477,7 +1453,7 @@
 }
 
 // Tests that an animation controller with only a pending observer gets ticked
-// but doesn't progress animations past the Starting state.
+// but doesn't progress animations past the STARTING state.
 TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
   scoped_ptr<AnimationEventsVector> events(
       make_scoped_ptr(new AnimationEventsVector));
@@ -1487,49 +1463,49 @@
       LayerAnimationController::Create(0));
 
   const int id = 1;
-  controller->AddAnimation(CreateAnimation(scoped_ptr<AnimationCurve>(
-      new FakeFloatTransition(1.0, 0.5f, 1.f)).Pass(),
-      id,
-      Animation::Opacity));
+  controller->AddAnimation(CreateAnimation(
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.5f, 1.f))
+          .Pass(),
+      id, Animation::OPACITY));
 
-  // Without an observer, the animation shouldn't progress to the Starting
+  // Without an observer, the animation shouldn't progress to the STARTING
   // state.
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->AddValueObserver(&pending_dummy);
 
   // With only a pending observer, the animation should progress to the
-  // Starting state and get ticked at its starting point, but should not
-  // progress to Running.
+  // STARTING state and get ticked at its starting point, but should not
+  // progress to RUNNING.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::Starting,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::STARTING,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(0.5f, pending_dummy.opacity());
 
-  // Even when already in the Starting state, the animation should stay
+  // Even when already in the STARTING state, the animation should stay
   // there, and shouldn't be ticked past its starting point.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   controller->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::Starting,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::STARTING,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(0.5f, pending_dummy.opacity());
 
   controller->AddValueObserver(&dummy);
 
   // Now that an active observer has been added, the animation should still
-  // initially tick at its starting point, but should now progress to Running.
+  // initially tick at its starting point, but should now progress to RUNNING.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   controller->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->size());
-  EXPECT_EQ(Animation::Running,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::RUNNING,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(0.5f, pending_dummy.opacity());
   EXPECT_EQ(0.5f, dummy.opacity());
 
@@ -1554,7 +1530,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 1, 1, Animation::Transform));
+      Animation::Create(curve1.Pass(), 1, 1, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   scoped_ptr<KeyframedTransformAnimationCurve> curve2(
@@ -1567,7 +1543,7 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   gfx::BoxF box(1.f, 2.f, -1.f, 3.f, 4.f, 5.f);
@@ -1578,7 +1554,7 @@
             bounds.ToString());
 
   controller_impl->GetAnimationById(1)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only the unfinished animation should affect the animated bounds.
   EXPECT_TRUE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
@@ -1586,7 +1562,7 @@
             bounds.ToString());
 
   controller_impl->GetAnimationById(2)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // There are no longer any running animations.
   EXPECT_FALSE(controller_impl->HasTransformAnimationThatInflatesBounds());
@@ -1602,7 +1578,7 @@
   operations3.AppendMatrix(transform3);
   curve3->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations3, nullptr));
-  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
   EXPECT_FALSE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
 }
@@ -1619,40 +1595,40 @@
   // state.
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 1, 1,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2, 2, Animation::Opacity));
+      2, 2, Animation::OPACITY));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 3, 3,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(2.0)).Pass(), 4, 4,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      5, 5, Animation::Opacity));
+      5, 5, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, nullptr);
 
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(1)->run_state());
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(2)->run_state());
-  EXPECT_EQ(Animation::Running, controller->GetAnimationById(3)->run_state());
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(1)->run_state());
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(2)->run_state());
+  EXPECT_EQ(Animation::RUNNING, controller->GetAnimationById(3)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller->GetAnimationById(4)->run_state());
-  EXPECT_EQ(Animation::Running, controller->GetAnimationById(5)->run_state());
+  EXPECT_EQ(Animation::RUNNING, controller->GetAnimationById(5)->run_state());
 
-  controller->AbortAnimations(Animation::Transform);
+  controller->AbortAnimations(Animation::TRANSFORM);
 
-  // Only un-finished Transform animations should have been aborted.
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(1)->run_state());
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(2)->run_state());
-  EXPECT_EQ(Animation::Aborted, controller->GetAnimationById(3)->run_state());
-  EXPECT_EQ(Animation::Aborted, controller->GetAnimationById(4)->run_state());
-  EXPECT_EQ(Animation::Running, controller->GetAnimationById(5)->run_state());
+  // Only un-finished TRANSFORM animations should have been aborted.
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(1)->run_state());
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(2)->run_state());
+  EXPECT_EQ(Animation::ABORTED, controller->GetAnimationById(3)->run_state());
+  EXPECT_EQ(Animation::ABORTED, controller->GetAnimationById(4)->run_state());
+  EXPECT_EQ(Animation::RUNNING, controller->GetAnimationById(5)->run_state());
 }
 
 // An animation aborted on the main thread should get deleted on both threads.
@@ -1673,17 +1649,17 @@
   controller_impl->ActivateAnimations();
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
 
-  controller->AbortAnimations(Animation::Opacity);
-  EXPECT_EQ(Animation::Aborted,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  controller->AbortAnimations(Animation::OPACITY);
+  EXPECT_EQ(Animation::ABORTED,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_FALSE(dummy.animation_waiting_for_deletion());
   EXPECT_FALSE(dummy_impl.animation_waiting_for_deletion());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
   EXPECT_TRUE(dummy.animation_waiting_for_deletion());
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
@@ -1709,9 +1685,9 @@
   controller_impl->ActivateAnimations();
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
 
-  controller_impl->AbortAnimations(Animation::Opacity);
-  EXPECT_EQ(Animation::Aborted,
-            controller_impl->GetAnimation(Animation::Opacity)->run_state());
+  controller_impl->AbortAnimations(Animation::OPACITY);
+  EXPECT_EQ(Animation::ABORTED,
+            controller_impl->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_FALSE(dummy.animation_waiting_for_deletion());
   EXPECT_FALSE(dummy_impl.animation_waiting_for_deletion());
 
@@ -1720,19 +1696,19 @@
   controller_impl->UpdateState(true, &events);
   EXPECT_TRUE(dummy_impl.animation_waiting_for_deletion());
   EXPECT_EQ(1u, events.size());
-  EXPECT_EQ(AnimationEvent::Aborted, events[0].type);
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller_impl->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(AnimationEvent::ABORTED, events[0].type);
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller_impl->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->NotifyAnimationAborted(events[0]);
-  EXPECT_EQ(Animation::Aborted,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::ABORTED,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   controller->UpdateState(true, nullptr);
   EXPECT_TRUE(dummy.animation_waiting_for_deletion());
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
@@ -1740,7 +1716,7 @@
   EXPECT_FALSE(controller_impl->GetAnimationById(animation_id));
 }
 
-// Ensure that we only generate Finished events for animations in a group
+// Ensure that we only generate FINISHED events for animations in a group
 // once all animations in that group are finished.
 TEST(LayerAnimationControllerTest, FinishedEventsForGroup) {
   scoped_ptr<AnimationEventsVector> events(
@@ -1755,18 +1731,18 @@
   // Add two animations with the same group id but different durations.
   controller_impl->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(2.0)).Pass(), 1,
-      group_id, Animation::Transform));
+      group_id, Animation::TRANSFORM));
   controller_impl->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2, group_id, Animation::Opacity));
+      2, group_id, Animation::OPACITY));
 
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
 
   // Both animations should have started.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Started, (*events)[0].type);
-  EXPECT_EQ(AnimationEvent::Started, (*events)[1].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[1].type);
 
   events.reset(new AnimationEventsVector);
   controller_impl->Animate(kInitialTickTime +
@@ -1774,25 +1750,25 @@
   controller_impl->UpdateState(true, events.get());
 
   // The opacity animation should be finished, but should not have generated
-  // a Finished event yet.
+  // a FINISHED event yet.
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::Finished,
+  EXPECT_EQ(Animation::FINISHED,
             controller_impl->GetAnimationById(2)->run_state());
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(1)->run_state());
 
   controller_impl->Animate(kInitialTickTime +
                            TimeDelta::FromMilliseconds(2000));
   controller_impl->UpdateState(true, events.get());
 
-  // Both animations should have generated Finished events.
+  // Both animations should have generated FINISHED events.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[0].type);
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[1].type);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[1].type);
 }
 
 // Ensure that when a group has a mix of aborted and finished animations,
-// we generate a Finished event for the finished animation and an Aborted
+// we generate a FINISHED event for the finished animation and an ABORTED
 // event for the aborted animation.
 TEST(LayerAnimationControllerTest, FinishedAndAbortedEventsForGroup) {
   scoped_ptr<AnimationEventsVector> events(
@@ -1804,36 +1780,34 @@
 
   // Add two animations with the same group id.
   controller_impl->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 1,
+      Animation::TRANSFORM));
   controller_impl->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
 
   // Both animations should have started.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Started, (*events)[0].type);
-  EXPECT_EQ(AnimationEvent::Started, (*events)[1].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[1].type);
 
-  controller_impl->AbortAnimations(Animation::Opacity);
+  controller_impl->AbortAnimations(Animation::OPACITY);
 
   events.reset(new AnimationEventsVector);
   controller_impl->Animate(kInitialTickTime +
                            TimeDelta::FromMilliseconds(1000));
   controller_impl->UpdateState(true, events.get());
 
-  // We should have exactly 2 events: a Finished event for the tranform
-  // animation, and an Aborted event for the opacity animation.
+  // We should have exactly 2 events: a FINISHED event for the tranform
+  // animation, and an ABORTED event for the opacity animation.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[0].type);
-  EXPECT_EQ(Animation::Transform, (*events)[0].target_property);
-  EXPECT_EQ(AnimationEvent::Aborted, (*events)[1].type);
-  EXPECT_EQ(Animation::Opacity, (*events)[1].target_property);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[0].type);
+  EXPECT_EQ(Animation::TRANSFORM, (*events)[0].target_property);
+  EXPECT_EQ(AnimationEvent::ABORTED, (*events)[1].type);
+  EXPECT_EQ(Animation::OPACITY, (*events)[1].target_property);
 }
 
 TEST(LayerAnimationControllerTest, HasAnimationThatAffectsScale) {
@@ -1844,8 +1818,7 @@
 
   controller_impl->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   // Opacity animations don't affect scale.
   EXPECT_FALSE(controller_impl->HasAnimationThatAffectsScale());
@@ -1861,7 +1834,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 2, 2, Animation::Transform));
+      Animation::Create(curve1.Pass(), 2, 2, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   // Translations don't affect scale.
@@ -1877,13 +1850,13 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_TRUE(controller_impl->HasAnimationThatAffectsScale());
 
   controller_impl->GetAnimationById(3)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by
   // HasAnimationThatAffectsScale.
@@ -1898,8 +1871,7 @@
 
   controller_impl->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   // Opacity animations aren't non-translation transforms.
   EXPECT_TRUE(controller_impl->HasOnlyTranslationTransforms());
@@ -1915,7 +1887,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 2, 2, Animation::Transform));
+      Animation::Create(curve1.Pass(), 2, 2, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   // The only transform animation we've added is a translation.
@@ -1931,14 +1903,14 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   // A scale animation is not a translation.
   EXPECT_FALSE(controller_impl->HasOnlyTranslationTransforms());
 
   controller_impl->GetAnimationById(3)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by
   // HasOnlyTranslationTransforms.
@@ -1964,7 +1936,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 1, 1, Animation::Transform));
+      Animation::Create(curve1.Pass(), 1, 1, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
@@ -1980,7 +1952,7 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
@@ -1996,15 +1968,15 @@
   curve3->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations3, nullptr));
 
-  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_FALSE(controller_impl->MaximumTargetScale(&max_scale));
 
   controller_impl->GetAnimationById(3)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   controller_impl->GetAnimationById(2)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by
   // MaximumTargetScale.
@@ -2028,7 +2000,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
   scoped_ptr<Animation> animation_owned(
-      Animation::Create(curve1.Pass(), 1, 1, Animation::Transform));
+      Animation::Create(curve1.Pass(), 1, 1, Animation::TRANSFORM));
   Animation* animation = animation_owned.get();
   controller_impl->AddAnimation(animation_owned.Pass());
 
@@ -2036,45 +2008,45 @@
 
   EXPECT_GT(animation->playback_rate(), 0.0);
 
-  // Normal direction with positive playback rate.
-  animation->set_direction(Animation::Normal);
+  // NORMAL direction with positive playback rate.
+  animation->set_direction(Animation::DIRECTION_NORMAL);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 
-  // Alternate direction with positive playback rate.
-  animation->set_direction(Animation::Alternate);
+  // ALTERNATE direction with positive playback rate.
+  animation->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 
-  // Reverse direction with positive playback rate.
-  animation->set_direction(Animation::Reverse);
+  // REVERSE direction with positive playback rate.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
-  // Alternate reverse direction.
-  animation->set_direction(Animation::Reverse);
+  // ALTERNATE reverse direction.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
   animation->set_playback_rate(-1.0);
 
-  // Normal direction with negative playback rate.
-  animation->set_direction(Animation::Normal);
+  // NORMAL direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_NORMAL);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
-  // Alternate direction with negative playback rate.
-  animation->set_direction(Animation::Alternate);
+  // ALTERNATE direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
-  // Reverse direction with negative playback rate.
-  animation->set_direction(Animation::Reverse);
+  // REVERSE direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 
-  // Alternate reverse direction with negative playback rate.
-  animation->set_direction(Animation::Reverse);
+  // ALTERNATE reverse direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 }
@@ -2103,7 +2075,7 @@
   EXPECT_TRUE(controller_impl->needs_to_start_animations_for_testing());
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id)
                   ->affects_pending_observers());
@@ -2114,9 +2086,9 @@
   EXPECT_FALSE(controller_impl->needs_to_start_animations_for_testing());
   controller_impl->UpdateState(true, events.get());
 
-  // Since the animation hasn't been activated, it should still be Starting
-  // rather than Running.
-  EXPECT_EQ(Animation::Starting,
+  // Since the animation hasn't been activated, it should still be STARTING
+  // rather than RUNNING.
+  EXPECT_EQ(Animation::STARTING,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   // Since the animation hasn't been activated, only the pending observer
@@ -2135,8 +2107,8 @@
   controller_impl->UpdateState(true, events.get());
 
   // Since the animation has been activated, it should have reached the
-  // Running state and the active observer should start to get ticked.
-  EXPECT_EQ(Animation::Running,
+  // RUNNING state and the active observer should start to get ticked.
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
   EXPECT_EQ(0.5f, dummy_impl.opacity());
@@ -2162,7 +2134,7 @@
   controller->PushAnimationUpdatesTo(controller_impl.get());
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id)
                   ->affects_pending_observers());
@@ -2185,8 +2157,8 @@
   controller_impl->UpdateState(true, events.get());
 
   // Since the animation has been activated, it should have reached the
-  // Running state.
-  EXPECT_EQ(Animation::Running,
+  // RUNNING state.
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   controller_impl->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
@@ -2249,7 +2221,7 @@
   controller_impl->ActivateAnimations();
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
   EXPECT_EQ(0.5f, dummy_impl.opacity());
@@ -2261,7 +2233,7 @@
 
   // Delete the animation on the main-thread controller.
   controller->RemoveAnimation(
-      controller->GetAnimation(Animation::Opacity)->id());
+      controller->GetAnimation(Animation::OPACITY)->id());
   controller->PushAnimationUpdatesTo(controller_impl.get());
 
   // The animation should no longer affect pending observers.
@@ -2310,7 +2282,7 @@
   // Remove the first animation from the main-thread controller, and add a
   // new animation affecting the same property.
   controller->RemoveAnimation(
-      controller->GetAnimation(Animation::Opacity)->id());
+      controller->GetAnimation(Animation::OPACITY)->id());
   int second_animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 1.f, 0.5f, true);
   controller->PushAnimationUpdatesTo(controller_impl.get());
@@ -2331,10 +2303,10 @@
 
   // The original animation should still be running, and the new animation
   // should be starting.
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(first_animation_id)->run_state());
   EXPECT_EQ(
-      Animation::Starting,
+      Animation::STARTING,
       controller_impl->GetAnimationById(second_animation_id)->run_state());
 
   // The active observer should have been ticked by the original animation,
@@ -2359,7 +2331,7 @@
   // The new animation should be running, and the active observer should have
   // been ticked at the new animation's starting point.
   EXPECT_EQ(
-      Animation::Running,
+      Animation::RUNNING,
       controller_impl->GetAnimationById(second_animation_id)->run_state());
   EXPECT_EQ(1.f, pending_dummy_impl.opacity());
   EXPECT_EQ(1.f, dummy_impl.opacity());
@@ -2373,15 +2345,14 @@
 
   scoped_ptr<Animation> animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->AddAnimation(animation.Pass());
   controller->Animate(kInitialTickTime);
-  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::Opacity));
+  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::OPACITY));
   controller->UpdateState(true, nullptr);
   EXPECT_TRUE(controller->HasActiveAnimation());
-  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::Opacity));
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Filter));
+  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::OPACITY));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::FILTER));
   EXPECT_EQ(0.f, dummy.opacity());
 }
 
@@ -2393,22 +2364,21 @@
 
   scoped_ptr<Animation> animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
-  animation->set_fill_mode(Animation::FillModeNone);
+      1, Animation::OPACITY));
+  animation->set_fill_mode(Animation::FILL_MODE_NONE);
   animation->set_time_offset(TimeDelta::FromMilliseconds(-2000));
   controller->AddAnimation(animation.Pass());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Opacity));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::OPACITY));
   EXPECT_TRUE(controller->HasActiveAnimation());
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Opacity));
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Filter));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::OPACITY));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::FILTER));
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   controller->UpdateState(true, nullptr);
-  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::Opacity));
+  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::OPACITY));
 }
 
 }  // namespace
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc
index 9567263..87a29c1 100644
--- a/cc/animation/scroll_offset_animation_curve.cc
+++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -90,7 +90,7 @@
 }
 
 AnimationCurve::CurveType ScrollOffsetAnimationCurve::Type() const {
-  return ScrollOffset;
+  return SCROLL_OFFSET;
 }
 
 scoped_ptr<AnimationCurve> ScrollOffsetAnimationCurve::Clone() const {
diff --git a/cc/animation/scroll_offset_animation_curve_unittest.cc b/cc/animation/scroll_offset_animation_curve_unittest.cc
index cb3e914..8aea905 100644
--- a/cc/animation/scroll_offset_animation_curve_unittest.cc
+++ b/cc/animation/scroll_offset_animation_curve_unittest.cc
@@ -68,7 +68,7 @@
   EXPECT_GT(curve->Duration().InSecondsF(), 0);
   EXPECT_LT(curve->Duration().InSecondsF(), 0.1);
 
-  EXPECT_EQ(AnimationCurve::ScrollOffset, curve->Type());
+  EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, curve->Type());
   EXPECT_EQ(duration, curve->Duration());
 
   EXPECT_VECTOR2DF_EQ(initial_value,
@@ -101,7 +101,7 @@
 
   scoped_ptr<AnimationCurve> clone(curve->Clone().Pass());
 
-  EXPECT_EQ(AnimationCurve::ScrollOffset, clone->Type());
+  EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, clone->Type());
   EXPECT_EQ(duration, clone->Duration());
 
   EXPECT_VECTOR2DF_EQ(initial_value,
diff --git a/cc/animation/transform_operation.cc b/cc/animation/transform_operation.cc
index e9ae86d..7421924 100644
--- a/cc/animation/transform_operation.cc
+++ b/cc/animation/transform_operation.cc
@@ -99,14 +99,14 @@
     return true;
 
   TransformOperation::Type interpolation_type =
-      TransformOperation::TransformOperationIdentity;
+      TransformOperation::TRANSFORM_OPERATION_IDENTITY;
   if (IsOperationIdentity(to))
     interpolation_type = from->type;
   else
     interpolation_type = to->type;
 
   switch (interpolation_type) {
-  case TransformOperation::TransformOperationTranslate: {
+    case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: {
     SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
     SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
     SkMScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
@@ -118,7 +118,7 @@
                         BlendSkMScalars(from_z, to_z, progress));
     break;
   }
-  case TransformOperation::TransformOperationRotate: {
+  case TransformOperation::TRANSFORM_OPERATION_ROTATE: {
     SkMScalar axis_x = 0;
     SkMScalar axis_y = 0;
     SkMScalar axis_z = 1;
@@ -140,7 +140,7 @@
     }
     break;
   }
-  case TransformOperation::TransformOperationScale: {
+  case TransformOperation::TRANSFORM_OPERATION_SCALE: {
     SkMScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
     SkMScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
     SkMScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
@@ -152,7 +152,7 @@
                     BlendSkMScalars(from_z, to_z, progress));
     break;
   }
-  case TransformOperation::TransformOperationSkew: {
+  case TransformOperation::TRANSFORM_OPERATION_SKEW: {
     SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
     SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
     SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
@@ -161,7 +161,7 @@
     result->SkewY(BlendSkMScalars(from_y, to_y, progress));
     break;
   }
-  case TransformOperation::TransformOperationPerspective: {
+  case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: {
     SkMScalar from_perspective_depth =
         IsOperationIdentity(from) ? std::numeric_limits<SkMScalar>::max()
                                   : from->perspective_depth;
@@ -180,7 +180,7 @@
     result->ApplyPerspectiveDepth(1.f / blended_perspective_depth);
     break;
   }
-  case TransformOperation::TransformOperationMatrix: {
+  case TransformOperation::TRANSFORM_OPERATION_MATRIX: {
     gfx::Transform to_matrix;
     if (!IsOperationIdentity(to))
       to_matrix = to->matrix;
@@ -192,7 +192,7 @@
       return false;
     break;
   }
-  case TransformOperation::TransformOperationIdentity:
+  case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
     // Do nothing.
     break;
   }
@@ -375,20 +375,20 @@
   }
 
   TransformOperation::Type interpolation_type =
-      TransformOperation::TransformOperationIdentity;
+      TransformOperation::TRANSFORM_OPERATION_IDENTITY;
   if (is_identity_to)
     interpolation_type = from->type;
   else
     interpolation_type = to->type;
 
   switch (interpolation_type) {
-    case TransformOperation::TransformOperationIdentity:
+    case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
       *bounds = box;
       return true;
-    case TransformOperation::TransformOperationTranslate:
-    case TransformOperation::TransformOperationSkew:
-    case TransformOperation::TransformOperationPerspective:
-    case TransformOperation::TransformOperationScale: {
+    case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
+    case TransformOperation::TRANSFORM_OPERATION_SKEW:
+    case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
+    case TransformOperation::TRANSFORM_OPERATION_SCALE: {
       gfx::Transform from_transform;
       gfx::Transform to_transform;
       if (!BlendTransformOperations(from, to, min_progress, &from_transform) ||
@@ -404,7 +404,7 @@
 
       return true;
     }
-    case TransformOperation::TransformOperationRotate: {
+    case TransformOperation::TRANSFORM_OPERATION_ROTATE: {
       SkMScalar axis_x = 0;
       SkMScalar axis_y = 0;
       SkMScalar axis_z = 1;
@@ -429,7 +429,7 @@
       }
       return true;
     }
-    case TransformOperation::TransformOperationMatrix:
+    case TransformOperation::TRANSFORM_OPERATION_MATRIX:
       return false;
   }
   NOTREACHED();
diff --git a/cc/animation/transform_operation.h b/cc/animation/transform_operation.h
index 345ff29..3ea5fc2 100644
--- a/cc/animation/transform_operation.h
+++ b/cc/animation/transform_operation.h
@@ -15,18 +15,16 @@
 
 struct TransformOperation {
   enum Type {
-    TransformOperationTranslate,
-    TransformOperationRotate,
-    TransformOperationScale,
-    TransformOperationSkew,
-    TransformOperationPerspective,
-    TransformOperationMatrix,
-    TransformOperationIdentity
+    TRANSFORM_OPERATION_TRANSLATE,
+    TRANSFORM_OPERATION_ROTATE,
+    TRANSFORM_OPERATION_SCALE,
+    TRANSFORM_OPERATION_SKEW,
+    TRANSFORM_OPERATION_PERSPECTIVE,
+    TRANSFORM_OPERATION_MATRIX,
+    TRANSFORM_OPERATION_IDENTITY
   };
 
-  TransformOperation()
-      : type(TransformOperationIdentity) {
-  }
+  TransformOperation() : type(TRANSFORM_OPERATION_IDENTITY) {}
 
   Type type;
   gfx::Transform matrix;
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index d9d58f2..20e8c6a 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -85,9 +85,9 @@
 
 bool TransformOperations::AffectsScale() const {
   for (size_t i = 0; i < operations_.size(); ++i) {
-    if (operations_[i].type == TransformOperation::TransformOperationScale)
+    if (operations_[i].type == TransformOperation::TRANSFORM_OPERATION_SCALE)
       return true;
-    if (operations_[i].type == TransformOperation::TransformOperationMatrix &&
+    if (operations_[i].type == TransformOperation::TRANSFORM_OPERATION_MATRIX &&
         !operations_[i].matrix.IsIdentityOrTranslation())
       return true;
   }
@@ -97,18 +97,18 @@
 bool TransformOperations::PreservesAxisAlignment() const {
   for (size_t i = 0; i < operations_.size(); ++i) {
     switch (operations_[i].type) {
-      case TransformOperation::TransformOperationIdentity:
-      case TransformOperation::TransformOperationTranslate:
-      case TransformOperation::TransformOperationScale:
+      case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
+      case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
+      case TransformOperation::TRANSFORM_OPERATION_SCALE:
         continue;
-      case TransformOperation::TransformOperationMatrix:
+      case TransformOperation::TRANSFORM_OPERATION_MATRIX:
         if (!operations_[i].matrix.IsIdentity() &&
             !operations_[i].matrix.IsScaleOrTranslation())
           return false;
         continue;
-      case TransformOperation::TransformOperationRotate:
-      case TransformOperation::TransformOperationSkew:
-      case TransformOperation::TransformOperationPerspective:
+      case TransformOperation::TRANSFORM_OPERATION_ROTATE:
+      case TransformOperation::TRANSFORM_OPERATION_SKEW:
+      case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
         return false;
     }
   }
@@ -118,17 +118,17 @@
 bool TransformOperations::IsTranslation() const {
   for (size_t i = 0; i < operations_.size(); ++i) {
     switch (operations_[i].type) {
-      case TransformOperation::TransformOperationIdentity:
-      case TransformOperation::TransformOperationTranslate:
+      case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
+      case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
         continue;
-      case TransformOperation::TransformOperationMatrix:
+      case TransformOperation::TRANSFORM_OPERATION_MATRIX:
         if (!operations_[i].matrix.IsIdentityOrTranslation())
           return false;
         continue;
-      case TransformOperation::TransformOperationRotate:
-      case TransformOperation::TransformOperationScale:
-      case TransformOperation::TransformOperationSkew:
-      case TransformOperation::TransformOperationPerspective:
+      case TransformOperation::TRANSFORM_OPERATION_ROTATE:
+      case TransformOperation::TRANSFORM_OPERATION_SCALE:
+      case TransformOperation::TRANSFORM_OPERATION_SKEW:
+      case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
         return false;
     }
   }
@@ -140,18 +140,18 @@
   bool has_scale_component = false;
   for (size_t i = 0; i < operations_.size(); ++i) {
     switch (operations_[i].type) {
-      case TransformOperation::TransformOperationIdentity:
-      case TransformOperation::TransformOperationTranslate:
+      case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
+      case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
         continue;
-      case TransformOperation::TransformOperationMatrix:
+      case TransformOperation::TRANSFORM_OPERATION_MATRIX:
         if (!operations_[i].matrix.IsIdentityOrTranslation())
           return false;
         continue;
-      case TransformOperation::TransformOperationRotate:
-      case TransformOperation::TransformOperationSkew:
-      case TransformOperation::TransformOperationPerspective:
+      case TransformOperation::TRANSFORM_OPERATION_ROTATE:
+      case TransformOperation::TRANSFORM_OPERATION_SKEW:
+      case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
         return false;
-      case TransformOperation::TransformOperationScale:
+      case TransformOperation::TRANSFORM_OPERATION_SCALE:
         if (has_scale_component)
           return false;
         has_scale_component = true;
@@ -191,7 +191,7 @@
                                           SkMScalar z) {
   TransformOperation to_add;
   to_add.matrix.Translate3d(x, y, z);
-  to_add.type = TransformOperation::TransformOperationTranslate;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_TRANSLATE;
   to_add.translate.x = x;
   to_add.translate.y = y;
   to_add.translate.z = z;
@@ -205,7 +205,7 @@
                                        SkMScalar degrees) {
   TransformOperation to_add;
   to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
-  to_add.type = TransformOperation::TransformOperationRotate;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_ROTATE;
   to_add.rotate.axis.x = x;
   to_add.rotate.axis.y = y;
   to_add.rotate.axis.z = z;
@@ -217,7 +217,7 @@
 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
   TransformOperation to_add;
   to_add.matrix.Scale3d(x, y, z);
-  to_add.type = TransformOperation::TransformOperationScale;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_SCALE;
   to_add.scale.x = x;
   to_add.scale.y = y;
   to_add.scale.z = z;
@@ -229,7 +229,7 @@
   TransformOperation to_add;
   to_add.matrix.SkewX(x);
   to_add.matrix.SkewY(y);
-  to_add.type = TransformOperation::TransformOperationSkew;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEW;
   to_add.skew.x = x;
   to_add.skew.y = y;
   operations_.push_back(to_add);
@@ -239,7 +239,7 @@
 void TransformOperations::AppendPerspective(SkMScalar depth) {
   TransformOperation to_add;
   to_add.matrix.ApplyPerspectiveDepth(depth);
-  to_add.type = TransformOperation::TransformOperationPerspective;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE;
   to_add.perspective_depth = depth;
   operations_.push_back(to_add);
   decomposed_transform_dirty_ = true;
@@ -248,7 +248,7 @@
 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
   TransformOperation to_add;
   to_add.matrix = matrix;
-  to_add.type = TransformOperation::TransformOperationMatrix;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_MATRIX;
   operations_.push_back(to_add);
   decomposed_transform_dirty_ = true;
 }
diff --git a/cc/base/latency_info_swap_promise_monitor.cc b/cc/base/latency_info_swap_promise_monitor.cc
index dc28739..de065d1 100644
--- a/cc/base/latency_info_swap_promise_monitor.cc
+++ b/cc/base/latency_info_swap_promise_monitor.cc
@@ -12,12 +12,14 @@
 
 namespace {
 
-bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info) {
-  if (latency_info->FindLatency(
-          ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, nullptr))
+bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info,
+                                    bool on_main) {
+  ui::LatencyComponentType type = on_main ?
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT :
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT;
+  if (latency_info->FindLatency(type, 0, nullptr))
     return false;
-  latency_info->AddLatencyNumber(
-      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0);
+  latency_info->AddLatencyNumber(type, 0, 0);
   return true;
 }
 
@@ -48,14 +50,14 @@
 LatencyInfoSwapPromiseMonitor::~LatencyInfoSwapPromiseMonitor() {}
 
 void LatencyInfoSwapPromiseMonitor::OnSetNeedsCommitOnMain() {
-  if (AddRenderingScheduledComponent(latency_)) {
+  if (AddRenderingScheduledComponent(latency_, true /* on_main */)) {
     scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
     layer_tree_host_->QueueSwapPromise(swap_promise.Pass());
   }
 }
 
 void LatencyInfoSwapPromiseMonitor::OnSetNeedsRedrawOnImpl() {
-  if (AddRenderingScheduledComponent(latency_)) {
+  if (AddRenderingScheduledComponent(latency_, false /* on_main */)) {
     scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
     layer_tree_host_impl_->active_tree()->QueueSwapPromise(swap_promise.Pass());
   }
diff --git a/cc/blink/cc_blink_tests.gyp b/cc/blink/cc_blink_tests.gyp
index 003d126..2e3fef5 100644
--- a/cc/blink/cc_blink_tests.gyp
+++ b/cc/blink/cc_blink_tests.gyp
@@ -20,10 +20,10 @@
         'cc_blink.gyp:cc_blink',
       ],
       'sources': [
+        '../../base/test/run_all_unittests.cc',
         'web_animation_unittest.cc',
         'web_float_animation_curve_unittest.cc',
         'web_layer_impl_fixed_bounds_unittest.cc',
-        '../../base/test/run_all_unittests.cc',
       ],
     }
   ],
diff --git a/cc/blink/web_animation_impl.cc b/cc/blink/web_animation_impl.cc
index 24fa4ec..705c33a 100644
--- a/cc/blink/web_animation_impl.cc
+++ b/cc/blink/web_animation_impl.cc
@@ -119,13 +119,13 @@
 blink::WebCompositorAnimation::Direction WebCompositorAnimationImpl::direction()
     const {
   switch (animation_->direction()) {
-    case cc::Animation::Normal:
+    case cc::Animation::DIRECTION_NORMAL:
       return DirectionNormal;
-    case cc::Animation::Reverse:
+    case cc::Animation::DIRECTION_REVERSE:
       return DirectionReverse;
-    case cc::Animation::Alternate:
+    case cc::Animation::DIRECTION_ALTERNATE:
       return DirectionAlternate;
-    case cc::Animation::AlternateReverse:
+    case cc::Animation::DIRECTION_ALTERNATE_REVERSE:
       return DirectionAlternateReverse;
     default:
       NOTREACHED();
@@ -136,16 +136,16 @@
 void WebCompositorAnimationImpl::setDirection(Direction direction) {
   switch (direction) {
     case DirectionNormal:
-      animation_->set_direction(cc::Animation::Normal);
+      animation_->set_direction(cc::Animation::DIRECTION_NORMAL);
       break;
     case DirectionReverse:
-      animation_->set_direction(cc::Animation::Reverse);
+      animation_->set_direction(cc::Animation::DIRECTION_REVERSE);
       break;
     case DirectionAlternate:
-      animation_->set_direction(cc::Animation::Alternate);
+      animation_->set_direction(cc::Animation::DIRECTION_ALTERNATE);
       break;
     case DirectionAlternateReverse:
-      animation_->set_direction(cc::Animation::AlternateReverse);
+      animation_->set_direction(cc::Animation::DIRECTION_ALTERNATE_REVERSE);
       break;
   }
 }
@@ -161,13 +161,13 @@
 blink::WebCompositorAnimation::FillMode WebCompositorAnimationImpl::fillMode()
     const {
   switch (animation_->fill_mode()) {
-    case cc::Animation::FillModeNone:
+    case cc::Animation::FILL_MODE_NONE:
       return FillModeNone;
-    case cc::Animation::FillModeForwards:
+    case cc::Animation::FILL_MODE_FORWARDS:
       return FillModeForwards;
-    case cc::Animation::FillModeBackwards:
+    case cc::Animation::FILL_MODE_BACKWARDS:
       return FillModeBackwards;
-    case cc::Animation::FillModeBoth:
+    case cc::Animation::FILL_MODE_BOTH:
       return FillModeBoth;
     default:
       NOTREACHED();
@@ -178,16 +178,16 @@
 void WebCompositorAnimationImpl::setFillMode(FillMode fill_mode) {
   switch (fill_mode) {
     case FillModeNone:
-      animation_->set_fill_mode(cc::Animation::FillModeNone);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_NONE);
       break;
     case FillModeForwards:
-      animation_->set_fill_mode(cc::Animation::FillModeForwards);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_FORWARDS);
       break;
     case FillModeBackwards:
-      animation_->set_fill_mode(cc::Animation::FillModeBackwards);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_BACKWARDS);
       break;
     case FillModeBoth:
-      animation_->set_fill_mode(cc::Animation::FillModeBoth);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_BOTH);
       break;
   }
 }
diff --git a/cc/blink/web_display_item_list_impl.cc b/cc/blink/web_display_item_list_impl.cc
index dc1d430..4941afe 100644
--- a/cc/blink/web_display_item_list_impl.cc
+++ b/cc/blink/web_display_item_list_impl.cc
@@ -35,14 +35,7 @@
 
 void WebDisplayItemListImpl::appendDrawingItem(const SkPicture* picture) {
   display_item_list_->AppendItem(cc::DrawingDisplayItem::Create(
-      skia::SharePtr(const_cast<SkPicture*>(picture)), gfx::PointF(0, 0)));
-}
-
-void WebDisplayItemListImpl::appendDrawingItem(
-    SkPicture* picture,
-    const blink::WebFloatPoint& location) {
-  display_item_list_->AppendItem(
-      cc::DrawingDisplayItem::Create(skia::SharePtr(picture), location));
+      skia::SharePtr(const_cast<SkPicture*>(picture))));
 }
 
 void WebDisplayItemListImpl::appendClipItem(
@@ -101,7 +94,6 @@
   display_item_list_->AppendItem(cc::EndTransparencyDisplayItem::Create());
 }
 
-#if FILTER_DISPLAY_ITEM_USES_FILTER_OPERATIONS
 void WebDisplayItemListImpl::appendFilterItem(
     const blink::WebFilterOperations& filters,
     const blink::WebFloatRect& bounds) {
@@ -110,22 +102,23 @@
   display_item_list_->AppendItem(
       cc::FilterDisplayItem::Create(filters_impl.AsFilterOperations(), bounds));
 }
-#else
-void WebDisplayItemListImpl::appendFilterItem(
-    SkImageFilter* filter,
-    const blink::WebFloatRect& bounds) {
-  cc::FilterOperations filter_operations;
-  filter_operations.Append(
-      cc::FilterOperation::CreateReferenceFilter(skia::SharePtr(filter)));
-  display_item_list_->AppendItem(
-      cc::FilterDisplayItem::Create(filter_operations, bounds));
-}
-#endif
 
 void WebDisplayItemListImpl::appendEndFilterItem() {
   display_item_list_->AppendItem(cc::EndFilterDisplayItem::Create());
 }
 
+void WebDisplayItemListImpl::appendScrollItem(
+    const blink::WebSize& scrollOffset,
+    ScrollContainerId) {
+  SkMatrix44 matrix;
+  matrix.setTranslate(-scrollOffset.width, -scrollOffset.height, 0);
+  appendTransformItem(matrix);
+}
+
+void WebDisplayItemListImpl::appendEndScrollItem() {
+  appendEndTransformItem();
+}
+
 WebDisplayItemListImpl::~WebDisplayItemListImpl() {
 }
 
diff --git a/cc/blink/web_display_item_list_impl.h b/cc/blink/web_display_item_list_impl.h
index f2ff8c6..8ec7a15 100644
--- a/cc/blink/web_display_item_list_impl.h
+++ b/cc/blink/web_display_item_list_impl.h
@@ -33,8 +33,6 @@
 
   // blink::WebDisplayItemList implementation.
   virtual void appendDrawingItem(const SkPicture*);
-  virtual void appendDrawingItem(SkPicture*,
-                                 const blink::WebFloatPoint& location);
   virtual void appendClipItem(
       const blink::WebRect& clip_rect,
       const blink::WebVector<SkRRect>& rounded_clip_rects);
@@ -50,14 +48,12 @@
   virtual void appendTransparencyItem(float opacity,
                                       blink::WebBlendMode blend_mode);
   virtual void appendEndTransparencyItem();
-#if FILTER_DISPLAY_ITEM_USES_FILTER_OPERATIONS
   virtual void appendFilterItem(const blink::WebFilterOperations& filters,
                                 const blink::WebFloatRect& bounds);
-#else
-  virtual void appendFilterItem(SkImageFilter* filter,
-                                const blink::WebFloatRect& bounds);
-#endif
   virtual void appendEndFilterItem();
+  virtual void appendScrollItem(const blink::WebSize& scrollOffset,
+                                ScrollContainerId);
+  virtual void appendEndScrollItem();
 
  private:
   scoped_refptr<cc::DisplayItemList> display_item_list_;
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index 2d5e459..0ef5ae7 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -397,17 +397,17 @@
 }
 
 static_assert(static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnNone) ==
-                  ScrollBlocksOnNone,
+                  SCROLL_BLOCKS_ON_NONE,
               "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 static_assert(static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnStartTouch) ==
-                  ScrollBlocksOnStartTouch,
+                  SCROLL_BLOCKS_ON_START_TOUCH,
               "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 static_assert(static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnWheelEvent) ==
-                  ScrollBlocksOnWheelEvent,
+                  SCROLL_BLOCKS_ON_WHEEL_EVENT,
               "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 static_assert(
     static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnScrollEvent) ==
-        ScrollBlocksOnScrollEvent,
+        SCROLL_BLOCKS_ON_SCROLL_EVENT,
     "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 
 void WebLayerImpl::setScrollBlocksOn(blink::WebScrollBlocksOn blocks) {
diff --git a/cc/cc.gyp b/cc/cc.gyp
index d04b51c..84a0930 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -52,8 +52,8 @@
         'animation/layer_animation_value_provider.h',
         'animation/scroll_offset_animation_curve.cc',
         'animation/scroll_offset_animation_curve.h',
-        'animation/scrollbar_animation_controller.h',
         'animation/scrollbar_animation_controller.cc',
+        'animation/scrollbar_animation_controller.h',
         'animation/scrollbar_animation_controller_linear_fade.cc',
         'animation/scrollbar_animation_controller_linear_fade.h',
         'animation/scrollbar_animation_controller_thinning.cc',
@@ -88,9 +88,9 @@
         'base/swap_promise.h',
         'base/swap_promise_monitor.cc',
         'base/swap_promise_monitor.h',
-        'base/synced_property.h',
         'base/switches.cc',
         'base/switches.h',
+        'base/synced_property.h',
         'base/tiling_data.cc',
         'base/tiling_data.h',
         'base/time_util.h',
@@ -120,12 +120,12 @@
         'debug/layer_tree_debug_state.h',
         'debug/micro_benchmark.cc',
         'debug/micro_benchmark.h',
-        'debug/micro_benchmark_impl.cc',
-        'debug/micro_benchmark_impl.h',
         'debug/micro_benchmark_controller.cc',
         'debug/micro_benchmark_controller.h',
         'debug/micro_benchmark_controller_impl.cc',
         'debug/micro_benchmark_controller_impl.h',
+        'debug/micro_benchmark_impl.cc',
+        'debug/micro_benchmark_impl.h',
         'debug/paint_time_counter.cc',
         'debug/paint_time_counter.h',
         'debug/picture_debug_util.cc',
@@ -151,10 +151,10 @@
         'debug/unittest_only_benchmark_impl.h',
         'input/input_handler.cc',
         'input/input_handler.h',
-        'input/page_scale_animation.cc',
-        'input/page_scale_animation.h',
         'input/layer_selection_bound.cc',
         'input/layer_selection_bound.h',
+        'input/page_scale_animation.cc',
+        'input/page_scale_animation.h',
         'input/scroll_elasticity_helper.cc',
         'input/scroll_elasticity_helper.h',
         'input/selection_bound_type.h',
@@ -273,8 +273,8 @@
         'output/copy_output_request.h',
         'output/copy_output_result.cc',
         'output/copy_output_result.h',
-        'output/delegated_frame_data.h',
         'output/delegated_frame_data.cc',
+        'output/delegated_frame_data.h',
         'output/delegating_renderer.cc',
         'output/delegating_renderer.h',
         'output/direct_renderer.cc',
@@ -285,8 +285,8 @@
         'output/filter_operations.h',
         'output/geometry_binding.cc',
         'output/geometry_binding.h',
-        'output/gl_frame_data.h',
         'output/gl_frame_data.cc',
+        'output/gl_frame_data.h',
         'output/gl_renderer.cc',
         'output/gl_renderer.h',
         'output/gl_renderer_draw_cache.cc',
@@ -334,8 +334,8 @@
         'quads/draw_quad.h',
         'quads/io_surface_draw_quad.cc',
         'quads/io_surface_draw_quad.h',
-        'quads/largest_draw_quad.h',
         'quads/largest_draw_quad.cc',
+        'quads/largest_draw_quad.h',
         'quads/list_container.cc',
         'quads/list_container.h',
         'quads/picture_draw_quad.cc',
@@ -362,10 +362,10 @@
         'quads/yuv_video_draw_quad.h',
         'resources/bitmap_content_layer_updater.cc',
         'resources/bitmap_content_layer_updater.h',
-        'resources/bitmap_tile_task_worker_pool.cc',
-        'resources/bitmap_tile_task_worker_pool.h',
         'resources/bitmap_skpicture_content_layer_updater.cc',
         'resources/bitmap_skpicture_content_layer_updater.h',
+        'resources/bitmap_tile_task_worker_pool.cc',
+        'resources/bitmap_tile_task_worker_pool.h',
         'resources/clip_display_item.cc',
         'resources/clip_display_item.h',
         'resources/clip_path_display_item.cc',
@@ -429,19 +429,13 @@
         'resources/raster_source.h',
         'resources/raster_source_helper.cc',
         'resources/raster_source_helper.h',
+        'resources/raster_tile_priority_queue.cc',
+        'resources/raster_tile_priority_queue.h',
         'resources/raster_tile_priority_queue_all.cc',
         'resources/raster_tile_priority_queue_all.h',
         'resources/raster_tile_priority_queue_required.cc',
         'resources/raster_tile_priority_queue_required.h',
-        'resources/raster_tile_priority_queue.cc',
-        'resources/raster_tile_priority_queue.h',
         'resources/rasterizer.h',
-        'resources/software_rasterizer.cc',
-        'resources/software_rasterizer.h',
-        'resources/tile_task_worker_pool.cc',
-        'resources/tile_task_worker_pool.h',
-        'resources/tile_task_runner.cc',
-        'resources/tile_task_runner.h',
         'resources/recording_source.h',
         'resources/release_callback.h',
         'resources/resource.cc',
@@ -474,6 +468,8 @@
         'resources/single_release_callback_impl.h',
         'resources/skpicture_content_layer_updater.cc',
         'resources/skpicture_content_layer_updater.h',
+        'resources/software_rasterizer.cc',
+        'resources/software_rasterizer.h',
         'resources/task_graph_runner.cc',
         'resources/task_graph_runner.h',
         'resources/texture_mailbox.cc',
@@ -490,6 +486,10 @@
         'resources/tile_manager.h',
         'resources/tile_priority.cc',
         'resources/tile_priority.h',
+        'resources/tile_task_runner.cc',
+        'resources/tile_task_runner.h',
+        'resources/tile_task_worker_pool.cc',
+        'resources/tile_task_worker_pool.h',
         'resources/tiling_set_eviction_queue.cc',
         'resources/tiling_set_eviction_queue.h',
         'resources/tiling_set_raster_queue_all.cc',
@@ -595,14 +595,14 @@
         'surfaces/surface.h',
         'surfaces/surface_aggregator.cc',
         'surfaces/surface_aggregator.h',
+        'surfaces/surface_display_output_surface.cc',
+        'surfaces/surface_display_output_surface.h',
         'surfaces/surface_factory.cc',
         'surfaces/surface_factory.h',
         'surfaces/surface_factory_client.h',
         'surfaces/surface_id.h',
         'surfaces/surface_id_allocator.cc',
         'surfaces/surface_id_allocator.h',
-        'surfaces/surface_display_output_surface.cc',
-        'surfaces/surface_display_output_surface.h',
         'surfaces/surface_manager.cc',
         'surfaces/surface_manager.h',
         'surfaces/surface_resource_holder.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 49337bc..4d29a60 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -32,8 +32,8 @@
       'layers/delegated_frame_resource_collection_unittest.cc',
       'layers/delegated_renderer_layer_impl_unittest.cc',
       'layers/delegated_renderer_layer_unittest.cc',
-      'layers/heads_up_display_unittest.cc',
       'layers/heads_up_display_layer_impl_unittest.cc',
+      'layers/heads_up_display_unittest.cc',
       'layers/io_surface_layer_impl_unittest.cc',
       'layers/layer_impl_unittest.cc',
       'layers/layer_iterator_unittest.cc',
@@ -47,15 +47,15 @@
       'layers/picture_image_layer_unittest.cc',
       'layers/picture_layer_impl_unittest.cc',
       'layers/picture_layer_unittest.cc',
-      'layers/render_surface_unittest.cc',
       'layers/render_surface_impl_unittest.cc',
+      'layers/render_surface_unittest.cc',
       'layers/scrollbar_layer_unittest.cc',
       'layers/solid_color_layer_impl_unittest.cc',
       'layers/solid_color_scrollbar_layer_impl_unittest.cc',
-      'layers/surface_layer_unittest.cc',
       'layers/surface_layer_impl_unittest.cc',
-      'layers/texture_layer_unittest.cc',
+      'layers/surface_layer_unittest.cc',
       'layers/texture_layer_impl_unittest.cc',
+      'layers/texture_layer_unittest.cc',
       'layers/tiled_layer_impl_unittest.cc',
       'layers/tiled_layer_unittest.cc',
       'layers/ui_resource_layer_impl_unittest.cc',
@@ -83,8 +83,8 @@
       'resources/picture_pile_impl_unittest.cc',
       'resources/picture_pile_unittest.cc',
       'resources/picture_unittest.cc',
+      'resources/platform_color_unittest.cc',
       'resources/prioritized_resource_unittest.cc',
-      'resources/tile_task_worker_pool_unittest.cc',
       'resources/resource_provider_unittest.cc',
       'resources/resource_update_controller_unittest.cc',
       'resources/scoped_gpu_raster_unittest.cc',
@@ -94,6 +94,7 @@
       'resources/texture_uploader_unittest.cc',
       'resources/tile_manager_unittest.cc',
       'resources/tile_priority_unittest.cc',
+      'resources/tile_task_worker_pool_unittest.cc',
       'resources/video_resource_updater_unittest.cc',
       'scheduler/begin_frame_source_unittest.cc',
       'scheduler/delay_based_time_source_unittest.cc',
@@ -110,7 +111,6 @@
       'trees/layer_tree_host_pixeltest_blending.cc',
       'trees/layer_tree_host_pixeltest_filters.cc',
       'trees/layer_tree_host_pixeltest_masks.cc',
-      'trees/layer_tree_host_pixeltest_on_demand_raster.cc',
       'trees/layer_tree_host_pixeltest_readback.cc',
       'trees/layer_tree_host_pixeltest_synchronous.cc',
       'trees/layer_tree_host_unittest.cc',
@@ -119,8 +119,8 @@
       'trees/layer_tree_host_unittest_copyrequest.cc',
       'trees/layer_tree_host_unittest_damage.cc',
       'trees/layer_tree_host_unittest_delegated.cc',
-      'trees/layer_tree_host_unittest_occlusion.cc',
       'trees/layer_tree_host_unittest_no_message_loop.cc',
+      'trees/layer_tree_host_unittest_occlusion.cc',
       'trees/layer_tree_host_unittest_picture.cc',
       'trees/layer_tree_host_unittest_proxy.cc',
       'trees/layer_tree_host_unittest_scroll.cc',
@@ -132,6 +132,7 @@
       'trees/tree_synchronizer_unittest.cc',
     ],
     'cc_surfaces_unit_tests_source_files': [
+      'surfaces/display_unittest.cc',
       'surfaces/surface_aggregator_test_helpers.cc',
       'surfaces/surface_aggregator_test_helpers.h',
       'surfaces/surface_aggregator_unittest.cc',
@@ -144,6 +145,8 @@
       'test/animation_test_common.h',
       'test/begin_frame_args_test.cc',
       'test/begin_frame_args_test.h',
+      'test/failure_output_surface.cc',
+      'test/failure_output_surface.h',
       'test/fake_content_layer.cc',
       'test/fake_content_layer.h',
       'test/fake_content_layer_client.cc',
@@ -198,12 +201,8 @@
       'test/fake_ui_resource_layer_tree_host_impl.h',
       'test/fake_video_frame_provider.cc',
       'test/fake_video_frame_provider.h',
-      'test/failure_output_surface.cc',
-      'test/failure_output_surface.h',
       'test/geometry_test_utils.cc',
       'test/geometry_test_utils.h',
-      'test/test_in_process_context_provider.cc',
-      'test/test_in_process_context_provider.h',
       'test/impl_side_painting_settings.h',
       'test/layer_test_common.cc',
       'test/layer_test_common.h',
@@ -254,6 +253,8 @@
       'test/test_gpu_memory_buffer_manager.h',
       'test/test_image_factory.cc',
       'test/test_image_factory.h',
+      'test/test_in_process_context_provider.cc',
+      'test/test_in_process_context_provider.h',
       'test/test_now_source.cc',
       'test/test_now_source.h',
       'test/test_occlusion_tracker.h',
@@ -290,8 +291,8 @@
         'cc_test_support',
       ],
       'sources': [
-        'test/run_all_unittests.cc',
         'test/cc_test_suite.cc',
+        'test/run_all_unittests.cc',
         '<@(cc_unit_tests_source_files)',
         '<@(cc_surfaces_unit_tests_source_files)',
       ],
@@ -349,9 +350,9 @@
         'layers/picture_layer_impl_perftest.cc',
         'resources/picture_layer_tiling_perftest.cc',
         'resources/picture_pile_impl_perftest.cc',
-        'resources/tile_task_worker_pool_perftest.cc',
         'resources/task_graph_runner_perftest.cc',
         'resources/tile_manager_perftest.cc',
+        'resources/tile_task_worker_pool_perftest.cc',
         'test/cc_test_suite.cc',
         'test/run_all_perftests.cc',
         'trees/layer_tree_host_common_perftest.cc',
diff --git a/cc/debug/debug_colors.cc b/cc/debug/debug_colors.cc
index daec5cc..38ecf54 100644
--- a/cc/debug/debug_colors.cc
+++ b/cc/debug/debug_colors.cc
@@ -114,9 +114,9 @@
   return Scale(2, tree_impl);
 }
 
-// Missing tile borders are red.
+// Missing tile borders are dark grey.
 SkColor DebugColors::MissingTileBorderColor() {
-  return SkColorSetARGB(100, 255, 0, 0);
+  return SkColorSetARGB(64, 64, 64, 0);
 }
 int DebugColors::MissingTileBorderWidth(const LayerTreeImpl* tree_impl) {
   return Scale(1, tree_impl);
@@ -130,11 +130,11 @@
   return Scale(1, tree_impl);
 }
 
-// Picture tile borders are dark grey.
-SkColor DebugColors::PictureTileBorderColor() {
-  return SkColorSetARGB(64, 64, 64, 0);
+// OOM tile borders are red.
+SkColor DebugColors::OOMTileBorderColor() {
+  return SkColorSetARGB(100, 255, 0, 0);
 }
-int DebugColors::PictureTileBorderWidth(const LayerTreeImpl* tree_impl) {
+int DebugColors::OOMTileBorderWidth(const LayerTreeImpl* tree_impl) {
   return Scale(1, tree_impl);
 }
 
diff --git a/cc/debug/debug_colors.h b/cc/debug/debug_colors.h
index 52dab85..8e4ec8d 100644
--- a/cc/debug/debug_colors.h
+++ b/cc/debug/debug_colors.h
@@ -56,8 +56,8 @@
   static SkColor SolidColorTileBorderColor();
   static int SolidColorTileBorderWidth(const LayerTreeImpl* tree_impl);
 
-  static SkColor PictureTileBorderColor();
-  static int PictureTileBorderWidth(const LayerTreeImpl* tree_impl);
+  static SkColor OOMTileBorderColor();
+  static int OOMTileBorderWidth(const LayerTreeImpl* tree_impl);
 
   static SkColor DirectPictureBorderColor();
   static int DirectPictureBorderWidth(const LayerTreeImpl* tree_impl);
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index d899a85..954f8d7 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -66,14 +66,14 @@
   // Note these are used in a histogram. Do not reorder or delete existing
   // entries.
   enum ScrollStatus {
-    ScrollOnMainThread = 0,
-    ScrollStarted,
-    ScrollIgnored,
-    ScrollUnknown,
+    SCROLL_ON_MAIN_THREAD = 0,
+    SCROLL_STARTED,
+    SCROLL_IGNORED,
+    SCROLL_UNKNOWN,
     // This must be the last entry.
     ScrollStatusCount
   };
-  enum ScrollInputType { Gesture, Wheel, NonBubblingGesture };
+  enum ScrollInputType { GESTURE, WHEEL, NON_BUBBLING_GESTURE };
 
   // Binds a client to this handler to receive notifications. Only one client
   // can be bound to an InputHandler. The client must live at least until the
@@ -81,10 +81,10 @@
   virtual void BindToClient(InputHandlerClient* client) = 0;
 
   // Selects a layer to be scrolled at a given point in viewport (logical
-  // pixel) coordinates. Returns ScrollStarted if the layer at the coordinates
-  // can be scrolled, ScrollOnMainThread if the scroll event should instead be
-  // delegated to the main thread, or ScrollIgnored if there is nothing to be
-  // scrolled at the given coordinates.
+  // pixel) coordinates. Returns SCROLL_STARTED if the layer at the coordinates
+  // can be scrolled, SCROLL_ON_MAIN_THREAD if the scroll event should instead
+  // be delegated to the main thread, or SCROLL_IGNORED if there is nothing to
+  // be scrolled at the given coordinates.
   virtual ScrollStatus ScrollBegin(const gfx::Point& viewport_point,
                                    ScrollInputType type) = 0;
 
@@ -102,7 +102,7 @@
   // If the scroll delta hits the root layer, and the layer can no longer move,
   // the root overscroll accumulated within this ScrollBegin() scope is reported
   // in the return value's |accumulated_overscroll| field.
-  // Should only be called if ScrollBegin() returned ScrollStarted.
+  // Should only be called if ScrollBegin() returned SCROLL_STARTED.
   virtual InputHandlerScrollResult ScrollBy(
       const gfx::Point& viewport_point,
       const gfx::Vector2dF& scroll_delta) = 0;
@@ -110,14 +110,14 @@
   virtual bool ScrollVerticallyByPage(const gfx::Point& viewport_point,
                                       ScrollDirection direction) = 0;
 
-  // Returns ScrollStarted if a layer was being actively being scrolled,
-  // ScrollIgnored if not.
+  // Returns SCROLL_STARTED if a layer was being actively being scrolled,
+  // SCROLL_IGNORED if not.
   virtual ScrollStatus FlingScrollBegin() = 0;
 
   virtual void MouseMoveAt(const gfx::Point& mouse_position) = 0;
 
   // Stop scrolling the selected layer. Should only be called if ScrollBegin()
-  // returned ScrollStarted.
+  // returned SCROLL_STARTED.
   virtual void ScrollEnd() = 0;
 
   virtual void SetRootLayerScrollOffsetDelegate(
diff --git a/cc/input/scroll_elasticity_helper.cc b/cc/input/scroll_elasticity_helper.cc
index 1e9fe7d..1c496d9 100644
--- a/cc/input/scroll_elasticity_helper.cc
+++ b/cc/input/scroll_elasticity_helper.cc
@@ -15,6 +15,7 @@
   explicit ScrollElasticityHelperImpl(LayerTreeHostImpl* layer_tree_host_impl);
   ~ScrollElasticityHelperImpl() override;
 
+  bool IsUserScrollable() const override;
   gfx::Vector2dF StretchAmount() const override;
   void SetStretchAmount(const gfx::Vector2dF& stretch_amount) override;
   gfx::ScrollOffset ScrollOffset() const override;
@@ -34,6 +35,14 @@
 ScrollElasticityHelperImpl::~ScrollElasticityHelperImpl() {
 }
 
+bool ScrollElasticityHelperImpl::IsUserScrollable() const {
+  LayerImpl* layer = layer_tree_host_impl_->OuterViewportScrollLayer();
+  if (!layer)
+    return false;
+  return layer->user_scrollable_horizontal() ||
+         layer->user_scrollable_vertical();
+}
+
 gfx::Vector2dF ScrollElasticityHelperImpl::StretchAmount() const {
   return layer_tree_host_impl_->active_tree()->elastic_overscroll()->Current(
       true);
diff --git a/cc/input/scroll_elasticity_helper.h b/cc/input/scroll_elasticity_helper.h
index bf444ee..a2bdbfe 100644
--- a/cc/input/scroll_elasticity_helper.h
+++ b/cc/input/scroll_elasticity_helper.h
@@ -52,6 +52,8 @@
 
   virtual ~ScrollElasticityHelper() {}
 
+  virtual bool IsUserScrollable() const = 0;
+
   // The amount that the view is stretched past the normal allowable bounds.
   virtual gfx::Vector2dF StretchAmount() const = 0;
   virtual void SetStretchAmount(const gfx::Vector2dF& stretch_amount) = 0;
diff --git a/cc/layers/content_layer.cc b/cc/layers/content_layer.cc
index 6af92ec..d1ff842 100644
--- a/cc/layers/content_layer.cc
+++ b/cc/layers/content_layer.cc
@@ -114,10 +114,6 @@
     updater_->SetOpaque(opaque);
 }
 
-bool ContentLayer::SupportsLCDText() const {
-  return true;
-}
-
 skia::RefPtr<SkPicture> ContentLayer::GetPicture() const {
   if (!DrawsContent())
     return skia::RefPtr<SkPicture>();
diff --git a/cc/layers/content_layer.h b/cc/layers/content_layer.h
index 22dd8fd..5d6ac15 100644
--- a/cc/layers/content_layer.h
+++ b/cc/layers/content_layer.h
@@ -46,8 +46,6 @@
 
   void SetContentsOpaque(bool contents_opaque) override;
 
-  bool SupportsLCDText() const override;
-
   skia::RefPtr<SkPicture> GetPicture() const override;
 
   void OnOutputSurfaceCreated() override;
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 97fddae..19217c9 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -97,7 +97,7 @@
   scoped_ptr<ScopedResource> resource =
       ScopedResource::Create(resource_provider);
   resource->Allocate(internal_content_bounds_,
-                     ResourceProvider::TextureHintImmutable, RGBA_8888);
+                     ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   resources_.push_back(resource.Pass());
 }
 
@@ -210,13 +210,10 @@
   size_t row_bytes = 0;
   const void* pixels = hud_surface_->getCanvas()->peekPixels(&info, &row_bytes);
   DCHECK(pixels);
-  gfx::Rect content_rect(internal_content_bounds_);
   DCHECK(info.colorType() == kN32_SkColorType);
-  resource_provider->SetPixels(resources_.back()->id(),
-                               static_cast<const uint8_t*>(pixels),
-                               content_rect,
-                               content_rect,
-                               gfx::Vector2d());
+  resource_provider->CopyToResource(resources_.back()->id(),
+                                    static_cast<const uint8_t*>(pixels),
+                                    internal_content_bounds_);
 }
 
 void HeadsUpDisplayLayerImpl::ReleaseResources() {
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 19b505b..ca0e3b8 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -71,7 +71,7 @@
       force_render_surface_(false),
       transform_is_invertible_(true),
       has_render_surface_(false),
-      scroll_blocks_on_(ScrollBlocksOnNone),
+      scroll_blocks_on_(SCROLL_BLOCKS_ON_NONE),
       background_color_(0),
       opacity_(1.f),
       blend_mode_(SkXfermode::kSrcOver_Mode),
@@ -471,7 +471,7 @@
 }
 
 bool Layer::FilterIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Filter);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::FILTER);
 }
 
 void Layer::SetBackgroundFilters(const FilterOperations& filters) {
@@ -491,7 +491,7 @@
 }
 
 bool Layer::OpacityIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Opacity);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::OPACITY);
 }
 
 bool Layer::OpacityCanAnimateOnImplThread() const {
@@ -601,7 +601,7 @@
 }
 
 bool Layer::TransformIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::TRANSFORM);
 }
 
 void Layer::SetScrollParent(Layer* parent) {
@@ -909,7 +909,7 @@
       draw_checkerboard_for_missing_tiles_);
   layer->SetDrawsContent(DrawsContent());
   layer->SetHideLayerAndSubtree(hide_layer_and_subtree_);
-  layer->SetHasRenderSurface(has_render_surface_);
+  layer->SetHasRenderSurface(has_render_surface_ || layer->HasCopyRequest());
   if (!layer->FilterIsAnimatingOnImplOnly() && !FilterIsAnimating())
     layer->SetFilters(filters_);
   DCHECK(!(FilterIsAnimating() && layer->FilterIsAnimatingOnImplOnly()));
@@ -1181,7 +1181,7 @@
   if (!layer_animation_controller_->animation_registrar())
     return false;
 
-  if (animation->target_property() == Animation::ScrollOffset &&
+  if (animation->target_property() == Animation::SCROLL_OFFSET &&
       !layer_animation_controller_->animation_registrar()
            ->supports_scroll_animations())
     return false;
@@ -1240,10 +1240,6 @@
   return layer_tree_host_->rendering_stats_instrumentation();
 }
 
-bool Layer::SupportsLCDText() const {
-  return false;
-}
-
 void Layer::RemoveFromScrollTree() {
   if (scroll_children_.get()) {
     std::set<Layer*> copy = *scroll_children_;
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 873f360..8eb7c3d 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -242,7 +242,6 @@
   bool screen_space_opacity_is_animating() const {
     return draw_properties_.screen_space_opacity_is_animating;
   }
-  bool can_use_lcd_text() const { return draw_properties_.can_use_lcd_text; }
   bool is_clipped() const { return draw_properties_.is_clipped; }
   gfx::Rect clip_rect() const { return draw_properties_.clip_rect; }
   gfx::Rect drawable_content_rect() const {
@@ -457,8 +456,6 @@
   float raster_scale() const { return raster_scale_; }
   bool raster_scale_is_unknown() const { return raster_scale_ == 0.f; }
 
-  virtual bool SupportsLCDText() const;
-
   void SetNeedsPushProperties();
   bool needs_push_properties() const { return needs_push_properties_; }
   bool descendant_needs_push_properties() const {
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 6e11644..63fd4dc 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -54,7 +54,7 @@
       should_scroll_on_main_thread_(false),
       have_wheel_event_handlers_(false),
       have_scroll_event_handlers_(false),
-      scroll_blocks_on_(ScrollBlocksOnNone),
+      scroll_blocks_on_(SCROLL_BLOCKS_ON_NONE),
       user_scrollable_horizontal_(true),
       user_scrollable_vertical_(true),
       stacking_order_changed_(false),
@@ -425,12 +425,12 @@
     ScrollBlocksOn effective_block_mode) const {
   if (should_scroll_on_main_thread()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread");
-    return InputHandler::ScrollOnMainThread;
+    return InputHandler::SCROLL_ON_MAIN_THREAD;
   }
 
   if (!screen_space_transform().IsInvertible()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Ignored NonInvertibleTransform");
-    return InputHandler::ScrollIgnored;
+    return InputHandler::SCROLL_IGNORED;
   }
 
   if (!non_fast_scrollable_region().IsEmpty()) {
@@ -440,7 +440,7 @@
     if (!screen_space_transform().GetInverse(&inverse_screen_space_transform)) {
       // TODO(shawnsingh): We shouldn't be applying a projection if screen space
       // transform is uninvertible here. Perhaps we should be returning
-      // ScrollOnMainThread in this case?
+      // SCROLL_ON_MAIN_THREAD in this case?
     }
 
     gfx::PointF hit_test_point_in_content_space =
@@ -456,25 +456,25 @@
             gfx::ToRoundedPoint(hit_test_point_in_layer_space))) {
       TRACE_EVENT0("cc",
                    "LayerImpl::tryScroll: Failed NonFastScrollableRegion");
-      return InputHandler::ScrollOnMainThread;
+      return InputHandler::SCROLL_ON_MAIN_THREAD;
     }
   }
 
   if (have_scroll_event_handlers() &&
-      effective_block_mode & ScrollBlocksOnScrollEvent) {
+      effective_block_mode & SCROLL_BLOCKS_ON_SCROLL_EVENT) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed ScrollEventHandlers");
-    return InputHandler::ScrollOnMainThread;
+    return InputHandler::SCROLL_ON_MAIN_THREAD;
   }
 
-  if (type == InputHandler::Wheel && have_wheel_event_handlers() &&
-      effective_block_mode & ScrollBlocksOnWheelEvent) {
+  if (type == InputHandler::WHEEL && have_wheel_event_handlers() &&
+      effective_block_mode & SCROLL_BLOCKS_ON_WHEEL_EVENT) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed WheelEventHandlers");
-    return InputHandler::ScrollOnMainThread;
+    return InputHandler::SCROLL_ON_MAIN_THREAD;
   }
 
   if (!scrollable()) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
-    return InputHandler::ScrollIgnored;
+    return InputHandler::SCROLL_IGNORED;
   }
 
   gfx::ScrollOffset max_scroll_offset = MaxScrollOffset();
@@ -482,10 +482,10 @@
     TRACE_EVENT0("cc",
                  "LayerImpl::tryScroll: Ignored. Technically scrollable,"
                  " but has no affordance in either direction.");
-    return InputHandler::ScrollIgnored;
+    return InputHandler::SCROLL_IGNORED;
   }
 
-  return InputHandler::ScrollStarted;
+  return InputHandler::SCROLL_STARTED;
 }
 
 gfx::Rect LayerImpl::LayerRectToContentRect(
@@ -517,7 +517,7 @@
       draw_checkerboard_for_missing_tiles_);
   layer->SetDrawsContent(DrawsContent());
   layer->SetHideLayerAndSubtree(hide_layer_and_subtree_);
-  layer->SetHasRenderSurface(!!render_surface());
+  layer->SetHasRenderSurface(!!render_surface() || layer->HasCopyRequest());
   layer->SetFilters(filters());
   layer->SetBackgroundFilters(background_filters());
   layer->SetMasksToBounds(masks_to_bounds_);
@@ -672,7 +672,7 @@
 
   result->SetBoolean("DrawsContent", draws_content_);
   result->SetBoolean("Is3dSorted", Is3dSorted());
-  result->SetDouble("Opacity", opacity());
+  result->SetDouble("OPACITY", opacity());
   result->SetBoolean("ContentsOpaque", contents_opaque_);
 
   if (scrollable())
@@ -689,11 +689,11 @@
 
   if (scroll_blocks_on_) {
     list = new base::ListValue;
-    if (scroll_blocks_on_ & ScrollBlocksOnStartTouch)
+    if (scroll_blocks_on_ & SCROLL_BLOCKS_ON_START_TOUCH)
       list->AppendString("StartTouch");
-    if (scroll_blocks_on_ & ScrollBlocksOnWheelEvent)
+    if (scroll_blocks_on_ & SCROLL_BLOCKS_ON_WHEEL_EVENT)
       list->AppendString("WheelEvent");
-    if (scroll_blocks_on_ & ScrollBlocksOnScrollEvent)
+    if (scroll_blocks_on_ & SCROLL_BLOCKS_ON_SCROLL_EVENT)
       list->AppendString("ScrollEvent");
     result->Set("ScrollBlocksOn", list);
   }
@@ -946,12 +946,12 @@
 }
 
 bool LayerImpl::FilterIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Filter);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::FILTER);
 }
 
 bool LayerImpl::FilterIsAnimatingOnImplOnly() const {
   Animation* filter_animation =
-      layer_animation_controller_->GetAnimation(Animation::Filter);
+      layer_animation_controller_->GetAnimation(Animation::FILTER);
   return filter_animation && filter_animation->is_impl_only();
 }
 
@@ -989,12 +989,12 @@
 }
 
 bool LayerImpl::OpacityIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Opacity);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::OPACITY);
 }
 
 bool LayerImpl::OpacityIsAnimatingOnImplOnly() const {
   Animation* opacity_animation =
-      layer_animation_controller_->GetAnimation(Animation::Opacity);
+      layer_animation_controller_->GetAnimation(Animation::OPACITY);
   return opacity_animation && opacity_animation->is_impl_only();
 }
 
@@ -1066,12 +1066,12 @@
 }
 
 bool LayerImpl::TransformIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::TRANSFORM);
 }
 
 bool LayerImpl::TransformIsAnimatingOnImplOnly() const {
   Animation* transform_animation =
-      layer_animation_controller_->GetAnimation(Animation::Transform);
+      layer_animation_controller_->GetAnimation(Animation::TRANSFORM);
   return transform_animation && transform_animation->is_impl_only();
 }
 
@@ -1332,7 +1332,7 @@
 
 void LayerImpl::DidBecomeActive() {
   if (layer_tree_impl_->settings().scrollbar_animator ==
-      LayerTreeSettings::NoAnimator) {
+      LayerTreeSettings::NO_ANIMATOR) {
     return;
   }
 
@@ -1573,7 +1573,7 @@
     base::TimeTicks monotonic_time,
     Animation::TargetProperty target_property,
     int group) {
-  if (target_property == Animation::ScrollOffset)
+  if (target_property == Animation::SCROLL_OFFSET)
     layer_tree_impl_->InputScrollAnimationFinished();
 }
 
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 97fd205..5de0d9a 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -667,7 +667,7 @@
   bool have_wheel_event_handlers_ : 1;
   bool have_scroll_event_handlers_ : 1;
 
-  static_assert(ScrollBlocksOnMax < (1 << 3), "ScrollBlocksOn too big");
+  static_assert(SCROLL_BLOCKS_ON_MAX < (1 << 3), "ScrollBlocksOn too big");
   ScrollBlocksOn scroll_blocks_on_ : 3;
 
   bool user_scrollable_horizontal_ : 1;
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index 460726f..8a61510 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -738,8 +738,9 @@
                                    1.0,
                                    0,
                                    100);
-  impl_layer->layer_animation_controller()->GetAnimation(Animation::Transform)->
-      set_is_impl_only(true);
+  impl_layer->layer_animation_controller()
+      ->GetAnimation(Animation::TRANSFORM)
+      ->set_is_impl_only(true);
   transform.Rotate(45.0);
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTransform(transform));
 
@@ -779,8 +780,9 @@
                                    0.3f,
                                    0.7f,
                                    false);
-  impl_layer->layer_animation_controller()->GetAnimation(Animation::Opacity)->
-      set_is_impl_only(true);
+  impl_layer->layer_animation_controller()
+      ->GetAnimation(Animation::OPACITY)
+      ->set_is_impl_only(true);
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetOpacity(0.75f));
 
   EXPECT_FALSE(impl_layer->LayerPropertyChanged());
@@ -815,8 +817,9 @@
   impl_layer->ResetAllChangeTrackingForSubtree();
   AddAnimatedFilterToController(
       impl_layer->layer_animation_controller(), 1.0, 1.f, 2.f);
-  impl_layer->layer_animation_controller()->GetAnimation(Animation::Filter)->
-      set_is_impl_only(true);
+  impl_layer->layer_animation_controller()
+      ->GetAnimation(Animation::FILTER)
+      ->set_is_impl_only(true);
   filters.Append(FilterOperation::CreateSepiaFilter(0.5f));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetFilters(filters));
 
@@ -1148,7 +1151,7 @@
   curve->AddKeyframe(
       FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 0.7f, nullptr));
   scoped_ptr<Animation> animation =
-      Animation::Create(curve.Pass(), 0, 0, Animation::Opacity);
+      Animation::Create(curve.Pass(), 0, 0, Animation::OPACITY);
 
   return layer->AddAnimation(animation.Pass());
 }
diff --git a/cc/layers/picture_image_layer.cc b/cc/layers/picture_image_layer.cc
index 9f35d6e..6886612 100644
--- a/cc/layers/picture_image_layer.cc
+++ b/cc/layers/picture_image_layer.cc
@@ -73,8 +73,7 @@
   PaintContents(canvas, clip, painting_control);
 
   skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording());
-  display_item_list->AppendItem(
-      DrawingDisplayItem::Create(picture, gfx::Point()));
+  display_item_list->AppendItem(DrawingDisplayItem::Create(picture));
   return display_item_list;
 }
 
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc
index d40f555..3d2d38e 100644
--- a/cc/layers/picture_image_layer_impl_unittest.cc
+++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -52,9 +52,6 @@
       case PENDING_TREE:
         tree = host_impl_.pending_tree();
         break;
-      case NUM_TREES:
-        NOTREACHED();
-        break;
     }
     TestablePictureImageLayerImpl* layer =
         new TestablePictureImageLayerImpl(tree, id);
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 0ca2ae7..e7918e2 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -23,7 +23,6 @@
     : client_(client),
       instrumentation_object_tracker_(id()),
       update_source_frame_number_(-1),
-      can_use_lcd_text_for_update_(true),
       is_mask_(false),
       nearest_neighbor_(false) {
 }
@@ -69,8 +68,10 @@
 
   layer_impl->SetNearestNeighbor(nearest_neighbor_);
 
+  // Preserve lcd text settings from the current raster source.
+  bool can_use_lcd_text = layer_impl->RasterSourceUsesLCDText();
   scoped_refptr<RasterSource> raster_source =
-      recording_source_->CreateRasterSource();
+      recording_source_->CreateRasterSource(can_use_lcd_text);
   layer_impl->UpdateRasterSource(raster_source, &recording_invalidation_,
                                  nullptr);
   DCHECK(recording_invalidation_.IsEmpty());
@@ -107,14 +108,12 @@
   update_source_frame_number_ = layer_tree_host()->source_frame_number();
   bool updated = Layer::Update(queue, occlusion);
 
-  bool can_use_lcd_text_changed = UpdateCanUseLCDText();
-
   gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
       visible_content_rect(), 1.f / contents_scale_x());
   gfx::Size layer_size = paint_properties().bounds;
 
   if (last_updated_visible_content_rect_ == visible_content_rect() &&
-      recording_source_->GetSize() == layer_size && !can_use_lcd_text_changed &&
+      recording_source_->GetSize() == layer_size &&
       pending_invalidation_.IsEmpty()) {
     // Only early out if the visible content rect of this layer hasn't changed.
     return updated;
@@ -147,9 +146,8 @@
   // for them.
   DCHECK(client_);
   updated |= recording_source_->UpdateAndExpandInvalidation(
-      client_, &recording_invalidation_, can_use_lcd_text_for_update_,
-      layer_size, visible_layer_rect, update_source_frame_number_,
-      RecordingSource::RECORD_NORMALLY);
+      client_, &recording_invalidation_, layer_size, visible_layer_rect,
+      update_source_frame_number_, RecordingSource::RECORD_NORMALLY);
   last_updated_visible_content_rect_ = visible_content_rect();
 
   if (updated) {
@@ -167,20 +165,6 @@
   is_mask_ = is_mask;
 }
 
-bool PictureLayer::SupportsLCDText() const {
-  return true;
-}
-
-bool PictureLayer::UpdateCanUseLCDText() {
-  if (!can_use_lcd_text_for_update_)
-    return false;  // Don't allow the LCD text state to change once disabled.
-  if (can_use_lcd_text_for_update_ == can_use_lcd_text())
-    return false;
-
-  can_use_lcd_text_for_update_ = can_use_lcd_text();
-  return true;
-}
-
 skia::RefPtr<SkPicture> PictureLayer::GetPicture() const {
   // We could either flatten the RecordingSource into a single SkPicture,
   // or paint a fresh one depending on what we intend to do with the
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
index 648d1c9..767de27 100644
--- a/cc/layers/picture_layer.h
+++ b/cc/layers/picture_layer.h
@@ -33,7 +33,6 @@
   bool Update(ResourceUpdateQueue* queue,
               const OcclusionTracker<Layer>* occlusion) override;
   void SetIsMask(bool is_mask) override;
-  bool SupportsLCDText() const override;
   skia::RefPtr<SkPicture> GetPicture() const override;
   bool IsSuitableForGpuRasterization() const override;
 
@@ -56,8 +55,6 @@
   bool is_mask() const { return is_mask_; }
 
  private:
-  bool UpdateCanUseLCDText();
-
   ContentLayerClient* client_;
   scoped_ptr<RecordingSource> recording_source_;
   devtools_instrumentation::
@@ -69,7 +66,6 @@
   gfx::Rect last_updated_visible_content_rect_;
 
   int update_source_frame_number_;
-  bool can_use_lcd_text_for_update_;
   bool is_mask_;
   bool nearest_neighbor_;
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index f62a866..e819afd 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -225,9 +225,9 @@
         if (mode == TileDrawInfo::SOLID_COLOR_MODE) {
           color = DebugColors::SolidColorTileBorderColor();
           width = DebugColors::SolidColorTileBorderWidth(layer_tree_impl());
-        } else if (mode == TileDrawInfo::PICTURE_PILE_MODE) {
-          color = DebugColors::PictureTileBorderColor();
-          width = DebugColors::PictureTileBorderWidth(layer_tree_impl());
+        } else if (mode == TileDrawInfo::OOM_MODE) {
+          color = DebugColors::OOMTileBorderColor();
+          width = DebugColors::OOMTileBorderWidth(layer_tree_impl());
         } else if (iter.resolution() == HIGH_RESOLUTION) {
           color = DebugColors::HighResTileBorderColor();
           width = DebugColors::HighResTileBorderWidth(layer_tree_impl());
@@ -313,30 +313,6 @@
           has_draw_quad = true;
           break;
         }
-        case TileDrawInfo::PICTURE_PILE_MODE: {
-          if (!layer_tree_impl()
-                   ->GetRendererCapabilities()
-                   .allow_rasterize_on_demand) {
-            ++on_demand_missing_tile_count;
-            break;
-          }
-
-          gfx::RectF texture_rect = iter.texture_rect();
-
-          ResourceProvider* resource_provider =
-              layer_tree_impl()->resource_provider();
-          ResourceFormat format =
-              resource_provider->memory_efficient_texture_format();
-          PictureDrawQuad* quad =
-              render_pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
-          quad->SetNew(shared_quad_state, geometry_rect, opaque_rect,
-                       visible_geometry_rect, texture_rect,
-                       iter->desired_texture_size(), nearest_neighbor_, format,
-                       iter->content_rect(), iter->contents_scale(),
-                       raster_source_);
-          has_draw_quad = true;
-          break;
-        }
         case TileDrawInfo::SOLID_COLOR_MODE: {
           SolidColorDrawQuad* quad =
               render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -345,6 +321,8 @@
           has_draw_quad = true;
           break;
         }
+        case TileDrawInfo::OOM_MODE:
+          break;  // Checkerboard.
       }
     }
 
@@ -565,6 +543,36 @@
       MaximumContentsScale());
 }
 
+void PictureLayerImpl::UpdateCanUseLCDTextAfterCommit() {
+  // This function is only allowed to be called after commit, due to it not
+  // being smart about sharing tiles and because otherwise it would cause
+  // flashes by switching out tiles in place that may be currently on screen.
+  DCHECK(layer_tree_impl()->IsSyncTree());
+
+  // Don't allow the LCD text state to change once disabled.
+  if (!RasterSourceUsesLCDText())
+    return;
+  if (can_use_lcd_text() == RasterSourceUsesLCDText())
+    return;
+
+  // Raster sources are considered const, so in order to update the state
+  // a new one must be created and all tiles recreated.
+  scoped_refptr<RasterSource> new_raster_source =
+      raster_source_->CreateCloneWithoutLCDText();
+  // Synthetically invalidate everything.
+  gfx::Rect bounds_rect(bounds());
+  Region invalidation(bounds_rect);
+  UpdateRasterSource(new_raster_source, &invalidation, nullptr);
+  SetUpdateRect(bounds_rect);
+
+  DCHECK(!RasterSourceUsesLCDText());
+}
+
+bool PictureLayerImpl::RasterSourceUsesLCDText() const {
+  return raster_source_ ? raster_source_->CanUseLCDText()
+                        : layer_tree_impl()->settings().can_use_lcd_text;
+}
+
 void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
   if (layer_tree_impl()->IsActiveTree()) {
     gfx::RectF layer_damage_rect =
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index f32e56f..9500e22 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -79,6 +79,8 @@
                           Region* new_invalidation,
                           const PictureLayerTilingSet* pending_set);
   bool UpdateTiles(bool resourceless_software_draw);
+  void UpdateCanUseLCDTextAfterCommit();
+  bool RasterSourceUsesLCDText() const;
 
   // Mask-related functions.
   void GetContentsResourceId(ResourceProvider::ResourceId* resource_id,
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc
index 62f4129..ea73cb2 100644
--- a/cc/layers/picture_layer_impl_perftest.cc
+++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -72,7 +72,8 @@
                                              int num_tiles,
                                              const gfx::Size& viewport_size) {
     host_impl_.SetViewportSize(viewport_size);
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     timer_.Reset();
     do {
@@ -96,7 +97,8 @@
     host_impl_.SetViewportSize(viewport.size());
     pending_layer_->PushScrollOffsetFromMainThread(
         gfx::ScrollOffset(viewport.x(), viewport.y()));
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     timer_.Reset();
     do {
@@ -114,7 +116,8 @@
       int num_tiles,
       const gfx::Size& viewport_size) {
     host_impl_.SetViewportSize(viewport_size);
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     TreePriority priorities[] = {SAME_PRIORITY_FOR_BOTH_TREES,
                                  SMOOTHNESS_TAKES_PRIORITY,
@@ -145,7 +148,8 @@
     host_impl_.SetViewportSize(viewport.size());
     pending_layer_->PushScrollOffsetFromMainThread(
         gfx::ScrollOffset(viewport.x(), viewport.y()));
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     TreePriority priorities[] = {SAME_PRIORITY_FOR_BOTH_TREES,
                                  SMOOTHNESS_TAKES_PRIORITY,
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index f00b2e4..26f3be5 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -128,7 +128,8 @@
     active_layer_ = static_cast<FakePictureLayerImpl*>(
         host_impl_.active_tree()->LayerById(id_));
 
-    host_impl_.active_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
   }
 
   void SetupDefaultTreesWithFixedTileSize(const gfx::Size& layer_bounds,
@@ -223,7 +224,8 @@
         host_impl_.pending_tree()->LayerById(id_));
 
     // Add tilings/tiles for the layer.
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
   }
 
   void SetupDrawPropertiesAndUpdateTiles(FakePictureLayerImpl* layer,
@@ -431,7 +433,8 @@
                                         viewport_rect_for_tile_priority,
                                         transform_for_tile_priority,
                                         resourceless_software_draw);
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   gfx::Rect viewport_rect_for_tile_priority_in_view_space =
       viewport_rect_for_tile_priority;
@@ -465,7 +468,7 @@
                                         viewport_rect_for_tile_priority,
                                         transform_for_tile_priority,
                                         resourceless_software_draw);
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   gfx::Transform screen_to_view(gfx::Transform::kSkipInitialization);
   bool success = transform_for_tile_priority.GetInverse(&screen_to_view);
@@ -603,7 +606,8 @@
   host_impl_.SetExternalDrawConstraints(
       transform, viewport, viewport, viewport_rect_for_tile_priority,
       transform_for_tile_priority, resourceless_software_draw);
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   EXPECT_EQ(viewport_rect_for_tile_priority,
             active_layer_->viewport_rect_for_tile_priority_in_content_space());
@@ -621,7 +625,7 @@
   // should remain to be the previously cached value.
   EXPECT_EQ(viewport_rect_for_tile_priority,
             active_layer_->viewport_rect_for_tile_priority_in_content_space());
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   // Now the UpdateDrawProperties is called. The viewport rect for tile
   // priority should be the latest value.
@@ -1312,7 +1316,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   FakePictureLayerImpl* pending_mask =
       static_cast<FakePictureLayerImpl*>(pending_layer_->mask_layer());
@@ -1362,7 +1367,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // The mask tiling gets scaled down.
   EXPECT_LT(pending_mask->HighResTiling()->contents_scale(), 1.f);
@@ -1418,7 +1423,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   EXPECT_EQ(0u, pending_mask->num_tilings());
 }
@@ -1450,7 +1455,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   FakePictureLayerImpl* pending_mask =
       static_cast<FakePictureLayerImpl*>(pending_layer_->mask_layer());
@@ -1771,7 +1777,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // Set visible content rect that is different from
   // external_viewport_for_tile_priority.
@@ -1811,7 +1818,7 @@
 
   // Activate and draw active layer.
   host_impl_.ActivateSyncTree();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
   active_layer_->draw_properties().visible_content_rect = visible_content_rect;
 
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
@@ -2415,7 +2422,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
   EXPECT_TRUE(pending_layer_->tilings()->FindTilingWithScale(1.f));
 
   ActivateTree();
@@ -2596,8 +2604,8 @@
 // However, this is also a regression test for PictureLayerImpl in that
 // not having this update will cause a crash.
 TEST_F(DeferredInitPictureLayerImplTest, PreventUpdateTilesDuringLostContext) {
-  host_impl_.pending_tree()->UpdateDrawProperties();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(true);
+  host_impl_.active_tree()->UpdateDrawProperties(false);
   EXPECT_FALSE(host_impl_.pending_tree()->needs_update_draw_properties());
   EXPECT_FALSE(host_impl_.active_tree()->needs_update_draw_properties());
 
@@ -2609,7 +2617,7 @@
   // These will crash PictureLayerImpl if this is not true.
   ASSERT_TRUE(host_impl_.pending_tree()->needs_update_draw_properties());
   ASSERT_TRUE(host_impl_.active_tree()->needs_update_draw_properties());
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(false);
 }
 
 TEST_F(PictureLayerImplTest, HighResTilingDuringAnimationForCpuRasterization) {
@@ -3855,7 +3863,8 @@
   SetupPendingTree(pending_pile);
   pending_layer_->SetBounds(layer_bounds);
   ActivateTree();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
   std::vector<Tile*> tiles =
       active_layer_->HighResTiling()->AllTilesForTesting();
   host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles);
@@ -3905,7 +3914,7 @@
                                         WhichTree tree,
                                         size_t expected_occluded_tile_count) {
     WhichTree twin_tree = tree == ACTIVE_TREE ? PENDING_TREE : ACTIVE_TREE;
-    for (int priority_count = 0; priority_count < NUM_TREE_PRIORITIES;
+    for (int priority_count = 0; priority_count <= LAST_TREE_PRIORITY;
          ++priority_count) {
       TreePriority tree_priority = static_cast<TreePriority>(priority_count);
       size_t occluded_tile_count = 0u;
@@ -4006,9 +4015,6 @@
               // eviction queue.
               EXPECT_EQ(ACTIVE_TREE, tree);
               break;
-            case NUM_TREE_PRIORITIES:
-              NOTREACHED();
-              break;
           }
         }
         queue->Pop();
@@ -4068,7 +4074,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   unoccluded_tile_count = 0;
   queue.reset(new TilingSetRasterQueueAll(
@@ -4092,7 +4099,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   unoccluded_tile_count = 0;
   queue.reset(new TilingSetRasterQueueAll(
@@ -4166,7 +4173,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   for (size_t i = 0; i < pending_layer_->num_tilings(); ++i) {
     PictureLayerTiling* tiling = pending_layer_->tilings()->tiling_at(i);
@@ -4206,7 +4214,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   for (size_t i = 0; i < pending_layer_->num_tilings(); ++i) {
     PictureLayerTiling* tiling = pending_layer_->tilings()->tiling_at(i);
@@ -4280,7 +4288,8 @@
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
   // UpdateDrawProperties with the occluding layer.
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   EXPECT_EQ(5u, pending_layer_->num_tilings());
 
@@ -4479,7 +4488,8 @@
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
   // UpdateDrawProperties with the occluding layer.
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // The expected number of occluded tiles on each of the 2 tilings for each of
   // the 3 tree priorities.
@@ -4670,11 +4680,11 @@
 
   Region invalidation(layer_rect);
   recording_source->UpdateAndExpandInvalidation(
-      &client, &invalidation, false, layer_bounds, layer_rect, frame_number++,
+      &client, &invalidation, layer_bounds, layer_rect, frame_number++,
       RecordingSource::RECORD_NORMALLY);
 
   scoped_refptr<RasterSource> pending_raster_source =
-      recording_source->CreateRasterSource();
+      recording_source->CreateRasterSource(true);
 
   SetupPendingTreeWithFixedTileSize(pending_raster_source, tile_size, Region());
   ActivateTree();
@@ -4733,15 +4743,16 @@
 
   Region invalidation1(layer_rect);
   recording_source->UpdateAndExpandInvalidation(
-      &client, &invalidation1, false, layer_bounds, layer_rect, frame_number++,
+      &client, &invalidation1, layer_bounds, layer_rect, frame_number++,
       RecordingSource::RECORD_NORMALLY);
 
   scoped_refptr<RasterSource> raster_source1 =
-      recording_source->CreateRasterSource();
+      recording_source->CreateRasterSource(true);
 
   SetupPendingTree(raster_source1);
   ActivateTree();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   // We've started with a solid layer that contains some tilings.
   ASSERT_TRUE(active_layer_->tilings());
@@ -4751,11 +4762,11 @@
 
   Region invalidation2(layer_rect);
   recording_source->UpdateAndExpandInvalidation(
-      &client, &invalidation2, false, layer_bounds, layer_rect, frame_number++,
+      &client, &invalidation2, layer_bounds, layer_rect, frame_number++,
       RecordingSource::RECORD_NORMALLY);
 
   scoped_refptr<RasterSource> raster_source2 =
-      recording_source->CreateRasterSource();
+      recording_source->CreateRasterSource(true);
 
   SetupPendingTree(raster_source2);
   ActivateTree();
diff --git a/cc/layers/scroll_blocks_on.h b/cc/layers/scroll_blocks_on.h
index 2447298..f41e7fd 100644
--- a/cc/layers/scroll_blocks_on.h
+++ b/cc/layers/scroll_blocks_on.h
@@ -6,12 +6,13 @@
 #define CC_LAYERS_SCROLL_BLOCKS_ON_H_
 
 enum ScrollBlocksOn {
-  ScrollBlocksOnNone = 0x0,
-  ScrollBlocksOnStartTouch = 0x1,
-  ScrollBlocksOnWheelEvent = 0x2,
-  ScrollBlocksOnScrollEvent = 0x4,
-  ScrollBlocksOnMax = ScrollBlocksOnStartTouch | ScrollBlocksOnWheelEvent |
-                      ScrollBlocksOnScrollEvent
+  SCROLL_BLOCKS_ON_NONE = 0x0,
+  SCROLL_BLOCKS_ON_START_TOUCH = 0x1,
+  SCROLL_BLOCKS_ON_WHEEL_EVENT = 0x2,
+  SCROLL_BLOCKS_ON_SCROLL_EVENT = 0x4,
+  SCROLL_BLOCKS_ON_MAX = SCROLL_BLOCKS_ON_START_TOUCH |
+                         SCROLL_BLOCKS_ON_WHEEL_EVENT |
+                         SCROLL_BLOCKS_ON_SCROLL_EVENT
 };
 
 inline ScrollBlocksOn operator|(ScrollBlocksOn a, ScrollBlocksOn b) {
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index c70b93a..36c37e9 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -173,9 +173,10 @@
   // When the scrollbar is not an overlay scrollbar, the scroll should be
   // responded to on the main thread as the compositor does not yet implement
   // scrollbar scrolling.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            scrollbar_layer_impl->TryScroll(
-                gfx::Point(0, 0), InputHandler::Gesture, ScrollBlocksOnNone));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_MAIN_THREAD,
+      scrollbar_layer_impl->TryScroll(gfx::Point(0, 0), InputHandler::GESTURE,
+                                      SCROLL_BLOCKS_ON_NONE));
 
   // Create and attach an overlay scrollbar.
   scrollbar.reset(new FakeScrollbar(false, false, true));
@@ -187,9 +188,10 @@
 
   // The user shouldn't be able to drag an overlay scrollbar and the scroll
   // may be handled in the compositor.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            scrollbar_layer_impl->TryScroll(
-                gfx::Point(0, 0), InputHandler::Gesture, ScrollBlocksOnNone));
+  EXPECT_EQ(
+      InputHandler::SCROLL_IGNORED,
+      scrollbar_layer_impl->TryScroll(gfx::Point(0, 0), InputHandler::GESTURE,
+                                      SCROLL_BLOCKS_ON_NONE));
 }
 
 TEST_F(ScrollbarLayerTest, ScrollOffsetSynchronization) {
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
index d9ddee7..4f4b12e 100644
--- a/cc/layers/texture_layer_impl.cc
+++ b/cc/layers/texture_layer_impl.cc
@@ -105,7 +105,7 @@
 
     if (!texture_copy_->id()) {
       texture_copy_->Allocate(texture_mailbox_.shared_memory_size(),
-                              ResourceProvider::TextureHintImmutable,
+                              ResourceProvider::TEXTURE_HINT_IMMUTABLE,
                               resource_provider->best_texture_format());
     }
 
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 559e0ac..228f2cc 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -222,10 +222,13 @@
       DCHECK_GE(frame_resources_.size(), 3u);
       if (frame_resources_.size() < 3u)
         break;
-      YUVVideoDrawQuad::ColorSpace color_space =
-          frame_->format() == media::VideoFrame::YV12J
-              ? YUVVideoDrawQuad::REC_601_JPEG
-              : YUVVideoDrawQuad::REC_601;
+      YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::REC_601;
+      if (frame_->format() == media::VideoFrame::YV12J) {
+        color_space = YUVVideoDrawQuad::JPEG;
+      } else if (frame_->format() == media::VideoFrame::YV12HD) {
+        color_space = YUVVideoDrawQuad::REC_709;
+      }
+
       gfx::RectF tex_coord_rect(
           tex_x_offset, tex_y_offset, tex_width_scale, tex_height_scale);
       YUVVideoDrawQuad* yuv_video_quad =
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index 9fbd4a5..5867fce 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -409,7 +409,7 @@
                enlarge_pass_texture_amount_.y());
   if (!texture->id())
     texture->Allocate(
-        size, ResourceProvider::TextureHintImmutableFramebuffer, RGBA_8888);
+        size, ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER, RGBA_8888);
   DCHECK(texture->id());
 
   return BindFramebufferToTexture(frame, texture, render_pass->output_rect);
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 2612f10..5c560dc 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -84,54 +84,54 @@
 SamplerType SamplerTypeFromTextureTarget(GLenum target) {
   switch (target) {
     case GL_TEXTURE_2D:
-      return SamplerType2D;
+      return SAMPLER_TYPE_2D;
     case GL_TEXTURE_RECTANGLE_ARB:
-      return SamplerType2DRect;
+      return SAMPLER_TYPE_2D_RECT;
     case GL_TEXTURE_EXTERNAL_OES:
-      return SamplerTypeExternalOES;
+      return SAMPLER_TYPE_EXTERNAL_OES;
     default:
       NOTREACHED();
-      return SamplerType2D;
+      return SAMPLER_TYPE_2D;
   }
 }
 
 BlendMode BlendModeFromSkXfermode(SkXfermode::Mode mode) {
   switch (mode) {
     case SkXfermode::kSrcOver_Mode:
-      return BlendModeNormal;
+      return BLEND_MODE_NORMAL;
     case SkXfermode::kScreen_Mode:
-      return BlendModeScreen;
+      return BLEND_MODE_SCREEN;
     case SkXfermode::kOverlay_Mode:
-      return BlendModeOverlay;
+      return BLEND_MODE_OVERLAY;
     case SkXfermode::kDarken_Mode:
-      return BlendModeDarken;
+      return BLEND_MODE_DARKEN;
     case SkXfermode::kLighten_Mode:
-      return BlendModeLighten;
+      return BLEND_MODE_LIGHTEN;
     case SkXfermode::kColorDodge_Mode:
-      return BlendModeColorDodge;
+      return BLEND_MODE_COLOR_DODGE;
     case SkXfermode::kColorBurn_Mode:
-      return BlendModeColorBurn;
+      return BLEND_MODE_COLOR_BURN;
     case SkXfermode::kHardLight_Mode:
-      return BlendModeHardLight;
+      return BLEND_MODE_HARD_LIGHT;
     case SkXfermode::kSoftLight_Mode:
-      return BlendModeSoftLight;
+      return BLEND_MODE_SOFT_LIGHT;
     case SkXfermode::kDifference_Mode:
-      return BlendModeDifference;
+      return BLEND_MODE_DIFFERENCE;
     case SkXfermode::kExclusion_Mode:
-      return BlendModeExclusion;
+      return BLEND_MODE_EXCLUSION;
     case SkXfermode::kMultiply_Mode:
-      return BlendModeMultiply;
+      return BLEND_MODE_MULTIPLY;
     case SkXfermode::kHue_Mode:
-      return BlendModeHue;
+      return BLEND_MODE_HUE;
     case SkXfermode::kSaturation_Mode:
-      return BlendModeSaturation;
+      return BLEND_MODE_SATURATION;
     case SkXfermode::kColor_Mode:
-      return BlendModeColor;
+      return BLEND_MODE_COLOR;
     case SkXfermode::kLuminosity_Mode:
-      return BlendModeLuminosity;
+      return BLEND_MODE_LUMINOSITY;
     default:
       NOTREACHED();
-      return BlendModeNone;
+      return BLEND_MODE_NONE;
   }
 }
 
@@ -513,7 +513,8 @@
       DrawIOSurfaceQuad(frame, IOSurfaceDrawQuad::MaterialCast(quad));
       break;
     case DrawQuad::PICTURE_CONTENT:
-      DrawPictureQuad(frame, PictureDrawQuad::MaterialCast(quad));
+      // PictureDrawQuad should only be used for resourceless software draws.
+      NOTREACHED();
       break;
     case DrawQuad::RENDER_PASS:
       DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
@@ -845,7 +846,7 @@
       ScopedResource::Create(resource_provider_);
   // CopyTexImage2D fails when called on a texture having immutable storage.
   device_background_texture->Allocate(
-      bounding_rect.size(), ResourceProvider::TextureHintDefault, RGBA_8888);
+      bounding_rect.size(), ResourceProvider::TEXTURE_HINT_DEFAULT, RGBA_8888);
   {
     ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
                                              device_background_texture->id());
@@ -980,7 +981,7 @@
 
   scoped_ptr<ResourceProvider::ScopedSamplerGL> mask_resource_lock;
   unsigned mask_texture_id = 0;
-  SamplerType mask_sampler = SamplerTypeNA;
+  SamplerType mask_sampler = SAMPLER_TYPE_NA;
   if (quad->mask_resource_id) {
     mask_resource_lock.reset(new ResourceProvider::ScopedSamplerGL(
         resource_provider_, quad->mask_resource_id, GL_TEXTURE1, GL_LINEAR));
@@ -1031,7 +1032,7 @@
   DCHECK_EQ(background_texture || background_image, use_shaders_for_blending);
   BlendMode shader_blend_mode = use_shaders_for_blending
                                     ? BlendModeFromSkXfermode(blend_mode)
-                                    : BlendModeNone;
+                                    : BLEND_MODE_NONE;
 
   if (use_aa && mask_texture_id && !use_color_matrix) {
     const RenderPassMaskProgramAA* program = GetRenderPassMaskProgramAA(
@@ -1218,7 +1219,7 @@
     GLC(gl_, gl_->Uniform1i(shader_mask_sampler_location, 1));
 
     gfx::RectF mask_uv_rect = quad->MaskUVRect();
-    if (mask_sampler != SamplerType2D) {
+    if (mask_sampler != SAMPLER_TYPE_2D) {
       mask_uv_rect.Scale(quad->mask_texture_size.width(),
                          quad->mask_texture_size.height());
     }
@@ -1637,7 +1638,7 @@
   float fragment_tex_scale_y = clamp_tex_rect.height();
 
   // Map to normalized texture coordinates.
-  if (sampler != SamplerType2DRect) {
+  if (sampler != SAMPLER_TYPE_2D_RECT) {
     gfx::Size texture_size = quad->texture_size;
     DCHECK(!texture_size.IsEmpty());
     fragment_tex_translate_x /= texture_size.width();
@@ -1727,7 +1728,7 @@
   float vertex_tex_scale_y = tex_coord_rect.height();
 
   // Map to normalized texture coordinates.
-  if (sampler != SamplerType2DRect) {
+  if (sampler != SAMPLER_TYPE_2D_RECT) {
     gfx::Size texture_size = quad->texture_size;
     DCHECK(!texture_size.IsEmpty());
     vertex_tex_translate_x /= texture_size.width();
@@ -1899,9 +1900,12 @@
   float yuv_to_rgb_rec601[9] = {
       1.164f, 1.164f, 1.164f, 0.0f, -.391f, 2.018f, 1.596f, -.813f, 0.0f,
   };
-  float yuv_to_rgb_rec601_jpeg[9] = {
+  float yuv_to_rgb_jpeg[9] = {
       1.f, 1.f, 1.f, 0.0f, -.34414f, 1.772f, 1.402f, -.71414f, 0.0f,
   };
+  float yuv_to_rgb_rec709[9] = {
+      1.164f, 1.164f, 1.164f, 0.0f, -0.213f, 2.112f, 1.793f, -0.533f, 0.0f,
+  };
 
   // These values map to 16, 128, and 128 respectively, and are computed
   // as a fraction over 256 (e.g. 16 / 256 = 0.0625).
@@ -1909,12 +1913,12 @@
   //   Y - 16   : Gives 16 values of head and footroom for overshooting
   //   U - 128  : Turns unsigned U into signed U [-128,127]
   //   V - 128  : Turns unsigned V into signed V [-128,127]
-  float yuv_adjust_rec601[3] = {
+  float yuv_adjust_constrained[3] = {
       -0.0625f, -0.5f, -0.5f,
   };
 
   // Same as above, but without the head and footroom.
-  float yuv_adjust_rec601_jpeg[3] = {
+  float yuv_adjust_full[3] = {
       0.0f, -0.5f, -0.5f,
   };
 
@@ -1924,11 +1928,15 @@
   switch (quad->color_space) {
     case YUVVideoDrawQuad::REC_601:
       yuv_to_rgb = yuv_to_rgb_rec601;
-      yuv_adjust = yuv_adjust_rec601;
+      yuv_adjust = yuv_adjust_constrained;
       break;
-    case YUVVideoDrawQuad::REC_601_JPEG:
-      yuv_to_rgb = yuv_to_rgb_rec601_jpeg;
-      yuv_adjust = yuv_adjust_rec601_jpeg;
+    case YUVVideoDrawQuad::REC_709:
+      yuv_to_rgb = yuv_to_rgb_rec709;
+      yuv_adjust = yuv_adjust_constrained;
+      break;
+    case YUVVideoDrawQuad::JPEG:
+      yuv_to_rgb = yuv_to_rgb_jpeg;
+      yuv_adjust = yuv_adjust_full;
       break;
   }
 
@@ -1977,54 +1985,6 @@
                    program->vertex_shader().matrix_location());
 }
 
-void GLRenderer::DrawPictureQuad(const DrawingFrame* frame,
-                                 const PictureDrawQuad* quad) {
-  if (on_demand_tile_raster_bitmap_.width() != quad->texture_size.width() ||
-      on_demand_tile_raster_bitmap_.height() != quad->texture_size.height()) {
-    on_demand_tile_raster_bitmap_.allocN32Pixels(quad->texture_size.width(),
-                                                 quad->texture_size.height());
-
-    if (on_demand_tile_raster_resource_id_)
-      resource_provider_->DeleteResource(on_demand_tile_raster_resource_id_);
-
-    on_demand_tile_raster_resource_id_ = resource_provider_->CreateGLTexture(
-        quad->texture_size,
-        GL_TEXTURE_2D,
-        GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
-        GL_CLAMP_TO_EDGE,
-        ResourceProvider::TextureHintImmutable,
-        quad->texture_format);
-  }
-
-  SkCanvas canvas(on_demand_tile_raster_bitmap_);
-  quad->raster_source->PlaybackToCanvas(&canvas, quad->content_rect,
-                                        quad->contents_scale);
-
-  uint8_t* bitmap_pixels = NULL;
-  SkBitmap on_demand_tile_raster_bitmap_dest;
-  SkColorType colorType = ResourceFormatToSkColorType(quad->texture_format);
-  if (on_demand_tile_raster_bitmap_.colorType() != colorType) {
-    on_demand_tile_raster_bitmap_.copyTo(&on_demand_tile_raster_bitmap_dest,
-                                         colorType);
-    // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
-    // bitmap data. This check will be removed once crbug.com/293728 is fixed.
-    CHECK_EQ(0u, on_demand_tile_raster_bitmap_dest.rowBytes() % 4);
-    bitmap_pixels = reinterpret_cast<uint8_t*>(
-        on_demand_tile_raster_bitmap_dest.getPixels());
-  } else {
-    bitmap_pixels =
-        reinterpret_cast<uint8_t*>(on_demand_tile_raster_bitmap_.getPixels());
-  }
-
-  resource_provider_->SetPixels(on_demand_tile_raster_resource_id_,
-                                bitmap_pixels,
-                                gfx::Rect(quad->texture_size),
-                                gfx::Rect(quad->texture_size),
-                                gfx::Vector2d());
-
-  DrawContentQuad(frame, quad, on_demand_tile_raster_resource_id_);
-}
-
 struct TextureProgramBinding {
   template <class Program>
   void Set(Program* program) {
@@ -2775,8 +2735,8 @@
   if (!tile_checkerboard_program_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::checkerboardProgram::initalize");
     tile_checkerboard_program_.Initialize(output_surface_->context_provider(),
-                                          TexCoordPrecisionNA,
-                                          SamplerTypeNA);
+                                          TEX_COORD_PRECISION_NA,
+                                          SAMPLER_TYPE_NA);
   }
   return &tile_checkerboard_program_;
 }
@@ -2785,8 +2745,7 @@
   if (!debug_border_program_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::debugBorderProgram::initialize");
     debug_border_program_.Initialize(output_surface_->context_provider(),
-                                     TexCoordPrecisionNA,
-                                     SamplerTypeNA);
+                                     TEX_COORD_PRECISION_NA, SAMPLER_TYPE_NA);
   }
   return &debug_border_program_;
 }
@@ -2795,8 +2754,7 @@
   if (!solid_color_program_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::solidColorProgram::initialize");
     solid_color_program_.Initialize(output_surface_->context_provider(),
-                                    TexCoordPrecisionNA,
-                                    SamplerTypeNA);
+                                    TEX_COORD_PRECISION_NA, SAMPLER_TYPE_NA);
   }
   return &solid_color_program_;
 }
@@ -2805,8 +2763,7 @@
   if (!solid_color_program_aa_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::solidColorProgramAA::initialize");
     solid_color_program_aa_.Initialize(output_surface_->context_provider(),
-                                       TexCoordPrecisionNA,
-                                       SamplerTypeNA);
+                                       TEX_COORD_PRECISION_NA, SAMPLER_TYPE_NA);
   }
   return &solid_color_program_aa_;
 }
@@ -2815,16 +2772,14 @@
     TexCoordPrecision precision,
     BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassProgram* program = &render_pass_program_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2833,17 +2788,15 @@
     TexCoordPrecision precision,
     BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassProgramAA* program =
       &render_pass_program_aa_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2853,11 +2806,11 @@
     SamplerType sampler,
     BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskProgram* program =
       &render_pass_mask_program_[precision][sampler][blend_mode];
   if (!program->initialized()) {
@@ -2873,11 +2826,11 @@
                                        SamplerType sampler,
                                        BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskProgramAA* program =
       &render_pass_mask_program_aa_[precision][sampler][blend_mode];
   if (!program->initialized()) {
@@ -2892,17 +2845,15 @@
 GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision,
                                             BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassColorMatrixProgram* program =
       &render_pass_color_matrix_program_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgram::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2911,18 +2862,16 @@
 GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision,
                                               BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassColorMatrixProgramAA* program =
       &render_pass_color_matrix_program_aa_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc",
                  "GLRenderer::renderPassColorMatrixProgramAA::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2932,11 +2881,11 @@
                                                 SamplerType sampler,
                                                 BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskColorMatrixProgram* program =
       &render_pass_mask_color_matrix_program_[precision][sampler][blend_mode];
   if (!program->initialized()) {
@@ -2953,11 +2902,11 @@
                                                   SamplerType sampler,
                                                   BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskColorMatrixProgramAA* program =
       &render_pass_mask_color_matrix_program_aa_[precision][sampler]
                                                 [blend_mode];
@@ -2974,9 +2923,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgram* program = &tile_program_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgram::initialize");
@@ -2990,9 +2939,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramOpaque* program = &tile_program_opaque_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramOpaque::initialize");
@@ -3006,9 +2955,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramAA* program = &tile_program_aa_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramAA::initialize");
@@ -3022,9 +2971,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramSwizzle* program = &tile_program_swizzle_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzle::initialize");
@@ -3038,9 +2987,9 @@
 GLRenderer::GetTileProgramSwizzleOpaque(TexCoordPrecision precision,
                                         SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramSwizzleOpaque* program =
       &tile_program_swizzle_opaque_[precision][sampler];
   if (!program->initialized()) {
@@ -3055,9 +3004,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramSwizzleAA* program = &tile_program_swizzle_aa_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleAA::initialize");
@@ -3070,12 +3019,12 @@
 const GLRenderer::TextureProgram* GLRenderer::GetTextureProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   TextureProgram* program = &texture_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3083,14 +3032,14 @@
 const GLRenderer::NonPremultipliedTextureProgram*
 GLRenderer::GetNonPremultipliedTextureProgram(TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   NonPremultipliedTextureProgram* program =
       &nonpremultiplied_texture_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc",
                  "GLRenderer::NonPremultipliedTextureProgram::Initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3098,12 +3047,12 @@
 const GLRenderer::TextureBackgroundProgram*
 GLRenderer::GetTextureBackgroundProgram(TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   TextureBackgroundProgram* program = &texture_background_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3112,14 +3061,14 @@
 GLRenderer::GetNonPremultipliedTextureBackgroundProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   NonPremultipliedTextureBackgroundProgram* program =
       &nonpremultiplied_texture_background_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc",
                  "GLRenderer::NonPremultipliedTextureProgram::Initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3127,12 +3076,12 @@
 const GLRenderer::TextureProgram* GLRenderer::GetTextureIOSurfaceProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   TextureProgram* program = &texture_io_surface_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::textureIOSurfaceProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2DRect);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D_RECT);
   }
   return program;
 }
@@ -3140,12 +3089,12 @@
 const GLRenderer::VideoYUVProgram* GLRenderer::GetVideoYUVProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   VideoYUVProgram* program = &video_yuv_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::videoYUVProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3153,12 +3102,12 @@
 const GLRenderer::VideoYUVAProgram* GLRenderer::GetVideoYUVAProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   VideoYUVAProgram* program = &video_yuva_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::videoYUVAProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3168,13 +3117,13 @@
   if (!Capabilities().using_egl_image)
     return NULL;
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   VideoStreamTextureProgram* program =
       &video_stream_texture_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::streamTextureProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerTypeExternalOES);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_EXTERNAL_OES);
   }
   return program;
 }
@@ -3182,8 +3131,8 @@
 void GLRenderer::CleanupSharedObjects() {
   shared_geometry_ = nullptr;
 
-  for (int i = 0; i < NumTexCoordPrecisions; ++i) {
-    for (int j = 0; j < NumSamplerTypes; ++j) {
+  for (int i = 0; i <= LAST_TEX_COORD_PRECISION; ++i) {
+    for (int j = 0; j <= LAST_SAMPLER_TYPE; ++j) {
       tile_program_[i][j].Cleanup(gl_);
       tile_program_opaque_[i][j].Cleanup(gl_);
       tile_program_swizzle_[i][j].Cleanup(gl_);
@@ -3191,14 +3140,14 @@
       tile_program_aa_[i][j].Cleanup(gl_);
       tile_program_swizzle_aa_[i][j].Cleanup(gl_);
 
-      for (int k = 0; k < NumBlendModes; k++) {
+      for (int k = 0; k <= LAST_BLEND_MODE; k++) {
         render_pass_mask_program_[i][j][k].Cleanup(gl_);
         render_pass_mask_program_aa_[i][j][k].Cleanup(gl_);
         render_pass_mask_color_matrix_program_aa_[i][j][k].Cleanup(gl_);
         render_pass_mask_color_matrix_program_[i][j][k].Cleanup(gl_);
       }
     }
-    for (int j = 0; j < NumBlendModes; j++) {
+    for (int j = 0; j <= LAST_BLEND_MODE; j++) {
       render_pass_program_[i][j].Cleanup(gl_);
       render_pass_program_aa_[i][j].Cleanup(gl_);
       render_pass_color_matrix_program_[i][j].Cleanup(gl_);
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 22e7706..25c7096 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -376,47 +376,62 @@
   const SolidColorProgram* GetSolidColorProgram();
   const SolidColorProgramAA* GetSolidColorProgramAA();
 
-  TileProgram tile_program_[NumTexCoordPrecisions][NumSamplerTypes];
+  TileProgram
+      tile_program_[LAST_TEX_COORD_PRECISION + 1][LAST_SAMPLER_TYPE + 1];
   TileProgramOpaque
-      tile_program_opaque_[NumTexCoordPrecisions][NumSamplerTypes];
-  TileProgramAA tile_program_aa_[NumTexCoordPrecisions][NumSamplerTypes];
-  TileProgramSwizzle
-      tile_program_swizzle_[NumTexCoordPrecisions][NumSamplerTypes];
+      tile_program_opaque_[LAST_TEX_COORD_PRECISION + 1][LAST_SAMPLER_TYPE + 1];
+  TileProgramAA
+      tile_program_aa_[LAST_TEX_COORD_PRECISION + 1][LAST_SAMPLER_TYPE + 1];
+  TileProgramSwizzle tile_program_swizzle_[LAST_TEX_COORD_PRECISION +
+                                           1][LAST_SAMPLER_TYPE + 1];
   TileProgramSwizzleOpaque
-      tile_program_swizzle_opaque_[NumTexCoordPrecisions][NumSamplerTypes];
-  TileProgramSwizzleAA
-      tile_program_swizzle_aa_[NumTexCoordPrecisions][NumSamplerTypes];
+      tile_program_swizzle_opaque_[LAST_TEX_COORD_PRECISION +
+                                   1][LAST_SAMPLER_TYPE + 1];
+  TileProgramSwizzleAA tile_program_swizzle_aa_[LAST_TEX_COORD_PRECISION +
+                                                1][LAST_SAMPLER_TYPE + 1];
 
   TileCheckerboardProgram tile_checkerboard_program_;
 
-  TextureProgram texture_program_[NumTexCoordPrecisions];
+  TextureProgram texture_program_[LAST_TEX_COORD_PRECISION + 1];
   NonPremultipliedTextureProgram
-      nonpremultiplied_texture_program_[NumTexCoordPrecisions];
-  TextureBackgroundProgram texture_background_program_[NumTexCoordPrecisions];
+      nonpremultiplied_texture_program_[LAST_TEX_COORD_PRECISION + 1];
+  TextureBackgroundProgram
+      texture_background_program_[LAST_TEX_COORD_PRECISION + 1];
   NonPremultipliedTextureBackgroundProgram
-      nonpremultiplied_texture_background_program_[NumTexCoordPrecisions];
-  TextureProgram texture_io_surface_program_[NumTexCoordPrecisions];
+      nonpremultiplied_texture_background_program_[LAST_TEX_COORD_PRECISION +
+                                                   1];
+  TextureProgram texture_io_surface_program_[LAST_TEX_COORD_PRECISION + 1];
 
-  RenderPassProgram render_pass_program_[NumTexCoordPrecisions][NumBlendModes];
-  RenderPassProgramAA
-      render_pass_program_aa_[NumTexCoordPrecisions][NumBlendModes];
-  RenderPassMaskProgram render_pass_mask_program_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
-  RenderPassMaskProgramAA render_pass_mask_program_aa_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
+  RenderPassProgram
+      render_pass_program_[LAST_TEX_COORD_PRECISION + 1][LAST_BLEND_MODE + 1];
+  RenderPassProgramAA render_pass_program_aa_[LAST_TEX_COORD_PRECISION +
+                                              1][LAST_BLEND_MODE + 1];
+  RenderPassMaskProgram
+      render_pass_mask_program_[LAST_TEX_COORD_PRECISION +
+                                1][LAST_SAMPLER_TYPE + 1][LAST_BLEND_MODE + 1];
+  RenderPassMaskProgramAA
+      render_pass_mask_program_aa_[LAST_TEX_COORD_PRECISION +
+                                   1][LAST_SAMPLER_TYPE + 1][LAST_BLEND_MODE +
+                                                             1];
   RenderPassColorMatrixProgram
-      render_pass_color_matrix_program_[NumTexCoordPrecisions][NumBlendModes];
-  RenderPassColorMatrixProgramAA render_pass_color_matrix_program_aa_
-      [NumTexCoordPrecisions][NumBlendModes];
-  RenderPassMaskColorMatrixProgram render_pass_mask_color_matrix_program_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
-  RenderPassMaskColorMatrixProgramAA render_pass_mask_color_matrix_program_aa_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
+      render_pass_color_matrix_program_[LAST_TEX_COORD_PRECISION +
+                                        1][LAST_BLEND_MODE + 1];
+  RenderPassColorMatrixProgramAA
+      render_pass_color_matrix_program_aa_[LAST_TEX_COORD_PRECISION +
+                                           1][LAST_BLEND_MODE + 1];
+  RenderPassMaskColorMatrixProgram
+      render_pass_mask_color_matrix_program_[LAST_TEX_COORD_PRECISION +
+                                             1][LAST_SAMPLER_TYPE +
+                                                1][LAST_BLEND_MODE + 1];
+  RenderPassMaskColorMatrixProgramAA
+      render_pass_mask_color_matrix_program_aa_[LAST_TEX_COORD_PRECISION +
+                                                1][LAST_SAMPLER_TYPE +
+                                                   1][LAST_BLEND_MODE + 1];
 
-  VideoYUVProgram video_yuv_program_[NumTexCoordPrecisions];
-  VideoYUVAProgram video_yuva_program_[NumTexCoordPrecisions];
+  VideoYUVProgram video_yuv_program_[LAST_TEX_COORD_PRECISION + 1];
+  VideoYUVAProgram video_yuva_program_[LAST_TEX_COORD_PRECISION + 1];
   VideoStreamTextureProgram
-      video_stream_texture_program_[NumTexCoordPrecisions];
+      video_stream_texture_program_[LAST_TEX_COORD_PRECISION + 1];
 
   DebugBorderProgram debug_border_program_;
   SolidColorProgram solid_color_program_;
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index c5c1e4d..fc70900 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -57,41 +57,39 @@
 
 static inline SkXfermode::Mode BlendModeToSkXfermode(BlendMode blend_mode) {
   switch (blend_mode) {
-    case BlendModeNone:
-    case BlendModeNormal:
+    case BLEND_MODE_NONE:
+    case BLEND_MODE_NORMAL:
       return SkXfermode::kSrcOver_Mode;
-    case BlendModeScreen:
+    case BLEND_MODE_SCREEN:
       return SkXfermode::kScreen_Mode;
-    case BlendModeOverlay:
+    case BLEND_MODE_OVERLAY:
       return SkXfermode::kOverlay_Mode;
-    case BlendModeDarken:
+    case BLEND_MODE_DARKEN:
       return SkXfermode::kDarken_Mode;
-    case BlendModeLighten:
+    case BLEND_MODE_LIGHTEN:
       return SkXfermode::kLighten_Mode;
-    case BlendModeColorDodge:
+    case BLEND_MODE_COLOR_DODGE:
       return SkXfermode::kColorDodge_Mode;
-    case BlendModeColorBurn:
+    case BLEND_MODE_COLOR_BURN:
       return SkXfermode::kColorBurn_Mode;
-    case BlendModeHardLight:
+    case BLEND_MODE_HARD_LIGHT:
       return SkXfermode::kHardLight_Mode;
-    case BlendModeSoftLight:
+    case BLEND_MODE_SOFT_LIGHT:
       return SkXfermode::kSoftLight_Mode;
-    case BlendModeDifference:
+    case BLEND_MODE_DIFFERENCE:
       return SkXfermode::kDifference_Mode;
-    case BlendModeExclusion:
+    case BLEND_MODE_EXCLUSION:
       return SkXfermode::kExclusion_Mode;
-    case BlendModeMultiply:
+    case BLEND_MODE_MULTIPLY:
       return SkXfermode::kMultiply_Mode;
-    case BlendModeHue:
+    case BLEND_MODE_HUE:
       return SkXfermode::kHue_Mode;
-    case BlendModeSaturation:
+    case BLEND_MODE_SATURATION:
       return SkXfermode::kSaturation_Mode;
-    case BlendModeColor:
+    case BLEND_MODE_COLOR:
       return SkXfermode::kColor_Mode;
-    case BlendModeLuminosity:
+    case BLEND_MODE_LUMINOSITY:
       return SkXfermode::kLuminosity_Mode;
-    case NumBlendModes:
-      NOTREACHED();
   }
   return SkXfermode::kSrcOver_Mode;
 }
@@ -105,13 +103,13 @@
     EXPECT_PROGRAM_VALID(renderer()->GetDebugBorderProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgramAA());
-    TestShadersWithTexCoordPrecision(TexCoordPrecisionMedium);
-    TestShadersWithTexCoordPrecision(TexCoordPrecisionHigh);
+    TestShadersWithTexCoordPrecision(TEX_COORD_PRECISION_MEDIUM);
+    TestShadersWithTexCoordPrecision(TEX_COORD_PRECISION_HIGH);
     ASSERT_FALSE(renderer()->IsContextLost());
   }
 
   void TestShadersWithTexCoordPrecision(TexCoordPrecision precision) {
-    for (int i = 0; i < NumBlendModes; ++i) {
+    for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
       BlendMode blend_mode = static_cast<BlendMode>(i);
       EXPECT_PROGRAM_VALID(
           renderer()->GetRenderPassProgram(precision, blend_mode));
@@ -132,11 +130,11 @@
       EXPECT_PROGRAM_VALID(renderer()->GetVideoStreamTextureProgram(precision));
     else
       EXPECT_FALSE(renderer()->GetVideoStreamTextureProgram(precision));
-    TestShadersWithSamplerType(precision, SamplerType2D);
-    TestShadersWithSamplerType(precision, SamplerType2DRect);
+    TestShadersWithSamplerType(precision, SAMPLER_TYPE_2D);
+    TestShadersWithSamplerType(precision, SAMPLER_TYPE_2D_RECT);
     // This is unlikely to be ever true in tests due to usage of osmesa.
     if (renderer()->Capabilities().using_egl_image)
-      TestShadersWithSamplerType(precision, SamplerTypeExternalOES);
+      TestShadersWithSamplerType(precision, SAMPLER_TYPE_EXTERNAL_OES);
   }
 
   void TestShadersWithSamplerType(TexCoordPrecision precision,
@@ -149,7 +147,7 @@
         renderer()->GetTileProgramSwizzleOpaque(precision, sampler));
     EXPECT_PROGRAM_VALID(
         renderer()->GetTileProgramSwizzleAA(precision, sampler));
-    for (int i = 0; i < NumBlendModes; ++i) {
+    for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
       BlendMode blend_mode = static_cast<BlendMode>(i);
       EXPECT_PROGRAM_VALID(
           renderer()->GetRenderPassMaskProgram(precision, sampler, blend_mode));
@@ -1437,9 +1435,8 @@
   TestRenderPass* root_pass;
 
   ResourceProvider::ResourceId mask = resource_provider_->CreateResource(
-      gfx::Size(20, 12),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(20, 12), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider_->best_texture_format());
   resource_provider_->AllocateForTesting(mask);
 
@@ -1469,10 +1466,10 @@
   gfx::Transform transform_causing_aa;
   transform_causing_aa.Rotate(20.0);
 
-  for (int i = 0; i < NumBlendModes; ++i) {
+  for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
     BlendMode blend_mode = static_cast<BlendMode>(i);
     SkXfermode::Mode xfer_mode = BlendModeToSkXfermode(blend_mode);
-    settings_.force_blending_with_shaders = (blend_mode != BlendModeNone);
+    settings_.force_blending_with_shaders = (blend_mode != BLEND_MODE_NONE);
     // RenderPassProgram
     render_passes_in_draw_order_.clear();
     child_pass = AddRenderPass(&render_passes_in_draw_order_,
@@ -1499,7 +1496,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassProgram(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassColorMatrixProgram
     render_passes_in_draw_order_.clear();
@@ -1524,7 +1521,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassColorMatrixProgram(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassMaskProgram
     render_passes_in_draw_order_.clear();
@@ -1553,8 +1550,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskProgram(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskProgram(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
+                              blend_mode);
 
     // RenderPassMaskColorMatrixProgram
     render_passes_in_draw_order_.clear();
@@ -1579,8 +1576,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskColorMatrixProgram(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM,
+                                         SAMPLER_TYPE_2D, blend_mode);
 
     // RenderPassProgramAA
     render_passes_in_draw_order_.clear();
@@ -1609,7 +1606,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassProgramAA(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassColorMatrixProgramAA
     render_passes_in_draw_order_.clear();
@@ -1634,7 +1631,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassColorMatrixProgramAA(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassMaskProgramAA
     render_passes_in_draw_order_.clear();
@@ -1663,8 +1660,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskProgramAA(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskProgramAA(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
+                                blend_mode);
 
     // RenderPassMaskColorMatrixProgramAA
     render_passes_in_draw_order_.clear();
@@ -1689,8 +1686,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskColorMatrixProgramAA(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM,
+                                           SAMPLER_TYPE_2D, blend_mode);
   }
 }
 
@@ -1742,7 +1739,7 @@
 
   // If use_aa incorrectly ignores clipping, it will use the
   // RenderPassProgramAA shader instead of the RenderPassProgram.
-  TestRenderPassProgram(TexCoordPrecisionMedium, BlendModeNone);
+  TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, BLEND_MODE_NONE);
 }
 
 TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h
index a10d0ab..9c3244c 100644
--- a/cc/output/program_binding.h
+++ b/cc/output/program_binding.h
@@ -59,7 +59,7 @@
   void Initialize(ContextProvider* context_provider,
                   TexCoordPrecision precision,
                   SamplerType sampler,
-                  BlendMode blend_mode = BlendModeNone) {
+                  BlendMode blend_mode = BLEND_MODE_NONE) {
     DCHECK(context_provider);
     DCHECK(!initialized_);
 
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index d7d1211..9e9175e 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -123,19 +123,14 @@
                           SkColorGetR(texel_color),
                           SkColorGetG(texel_color),
                           SkColorGetB(texel_color));
-  std::vector<uint32_t> pixels(rect.size().GetArea(), pixel_color);
+  size_t num_pixels = static_cast<size_t>(rect.width()) * rect.height();
+  std::vector<uint32_t> pixels(num_pixels, pixel_color);
 
-  ResourceProvider::ResourceId resource =
-      resource_provider->CreateResource(rect.size(),
-                                        GL_CLAMP_TO_EDGE,
-                                        ResourceProvider::TextureHintImmutable,
-                                        RGBA_8888);
-  resource_provider->SetPixels(
-      resource,
-      reinterpret_cast<uint8_t*>(&pixels.front()),
-      rect,
-      rect,
-      gfx::Vector2d());
+  ResourceProvider::ResourceId resource = resource_provider->CreateResource(
+      rect.size(), GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      RGBA_8888);
+  resource_provider->CopyToResource(
+      resource, reinterpret_cast<uint8_t*>(&pixels.front()), rect.size());
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
 
@@ -162,6 +157,13 @@
 TYPED_TEST_CASE(RendererPixelTest, RendererTypes);
 
 template <typename RendererType>
+class SoftwareRendererPixelTest : public RendererPixelTest<RendererType> {};
+
+typedef ::testing::Types<SoftwareRenderer, SoftwareRendererWithExpandedViewport>
+    SoftwareRendererTypes;
+TYPED_TEST_CASE(SoftwareRendererPixelTest, SoftwareRendererTypes);
+
+template <typename RendererType>
 class FuzzyForSoftwareOnlyPixelComparator : public PixelComparator {
  public:
   explicit FuzzyForSoftwareOnlyPixelComparator(bool discard_alpha)
@@ -872,18 +874,13 @@
 
   ResourceProvider::ResourceId mask_resource_id =
       this->resource_provider_->CreateResource(
-          mask_rect.size(),
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          RGBA_8888);
+          mask_rect.size(), GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   {
     SkAutoLockPixels lock(bitmap);
-    this->resource_provider_->SetPixels(
-        mask_resource_id,
-        reinterpret_cast<uint8_t*>(bitmap.getPixels()),
-        mask_rect,
-        mask_rect,
-        gfx::Vector2d());
+    this->resource_provider_->CopyToResource(
+        mask_resource_id, reinterpret_cast<uint8_t*>(bitmap.getPixels()),
+        mask_rect.size());
   }
 
   // This RenderPassDrawQuad does not include the full |viewport_rect| which is
@@ -1379,7 +1376,7 @@
       FuzzyPixelOffByOneComparator(true)));
 }
 
-TYPED_TEST(RendererPixelTest, PictureDrawQuadIdentityScale) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadIdentityScale) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   // TODO(enne): the renderer should figure this out on its own.
@@ -1459,7 +1456,7 @@
 }
 
 // Not WithSkiaGPUBackend since that path currently requires tiles for opacity.
-TYPED_TEST(RendererPixelTest, PictureDrawQuadOpacity) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadOpacity) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   ResourceFormat texture_format = RGBA_8888;
@@ -1536,7 +1533,7 @@
 
 // If we disable image filtering, then a 2x2 bitmap should appear as four
 // huge sharp squares.
-TYPED_TEST(RendererPixelTest, PictureDrawQuadDisableImageFiltering) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadDisableImageFiltering) {
   // We only care about this in software mode since bilinear filtering is
   // cheap in hardware.
   if (!IsSoftwareRenderer<TypeParam>())
@@ -1593,7 +1590,7 @@
 }
 
 // This disables filtering by setting |nearest_neighbor| on the PictureDrawQuad.
-TYPED_TEST(RendererPixelTest, PictureDrawQuadNearestNeighbor) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNearestNeighbor) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   ResourceFormat texture_format = RGBA_8888;
@@ -1662,19 +1659,13 @@
   gfx::Size tile_size(2, 2);
   ResourceProvider::ResourceId resource =
       this->resource_provider_->CreateResource(
-          tile_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          tile_size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           RGBA_8888);
 
   {
     SkAutoLockPixels lock(bitmap);
-    this->resource_provider_->SetPixels(
-        resource,
-        static_cast<uint8_t*>(bitmap.getPixels()),
-        gfx::Rect(tile_size),
-        gfx::Rect(tile_size),
-        gfx::Vector2d());
+    this->resource_provider_->CopyToResource(
+        resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size);
   }
 
   RenderPassId id(1, 1);
@@ -1700,7 +1691,7 @@
       ExactPixelComparator(true)));
 }
 
-TYPED_TEST(RendererPixelTest, PictureDrawQuadNonIdentityScale) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNonIdentityScale) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   // TODO(enne): the renderer should figure this out on its own.
@@ -2001,45 +1992,6 @@
       &capture_rect));
 }
 
-TEST_F(GLRendererPixelTest, PictureDrawQuadTexture4444) {
-  gfx::Size pile_tile_size(1000, 1000);
-  gfx::Rect viewport(this->device_viewport_size_);
-  ResourceFormat texture_format = RGBA_4444;
-  bool nearest_neighbor = false;
-
-  RenderPassId id(1, 1);
-  gfx::Transform transform_to_root;
-  scoped_ptr<RenderPass> pass =
-      CreateTestRenderPass(id, viewport, transform_to_root);
-
-  // One viewport-filling blue quad
-  scoped_ptr<FakePicturePile> blue_recording =
-      FakePicturePile::CreateFilledPile(pile_tile_size, viewport.size());
-  SkPaint blue_paint;
-  blue_paint.setColor(SK_ColorBLUE);
-  blue_recording->add_draw_rect_with_paint(viewport, blue_paint);
-  blue_recording->RerecordPile();
-  scoped_refptr<FakePicturePileImpl> blue_pile =
-      FakePicturePileImpl::CreateFromPile(blue_recording.get(), nullptr);
-
-  gfx::Transform blue_content_to_target_transform;
-  SharedQuadState* blue_shared_state = CreateTestSharedQuadState(
-      blue_content_to_target_transform, viewport, pass.get());
-
-  PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
-  blue_quad->SetNew(blue_shared_state, viewport, gfx::Rect(), viewport,
-                    gfx::RectF(0.f, 0.f, 1.f, 1.f), viewport.size(),
-                    nearest_neighbor, texture_format, viewport, 1.f,
-                    blue_pile.get());
-
-  RenderPassList pass_list;
-  pass_list.push_back(pass.Pass());
-
-  EXPECT_TRUE(this->RunPixelTest(&pass_list,
-                                 base::FilePath(FILE_PATH_LITERAL("blue.png")),
-                                 ExactPixelComparator(true)));
-}
-
 TYPED_TEST(RendererPixelTest, WrapModeRepeat) {
   gfx::Rect rect(this->device_viewport_size_);
 
@@ -2049,7 +2001,7 @@
   SharedQuadState* shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
-  gfx::Rect texture_rect(4, 4);
+  gfx::Size texture_size(4, 4);
   SkPMColor colors[4] = {
     SkPreMultiplyColor(SkColorSetARGB(255, 0, 255, 0)),
     SkPreMultiplyColor(SkColorSetARGB(255, 0, 128, 0)),
@@ -2064,33 +2016,23 @@
   };
   ResourceProvider::ResourceId resource =
       this->resource_provider_->CreateResource(
-          texture_rect.size(),
-          GL_REPEAT,
-          ResourceProvider::TextureHintImmutable,
+          texture_size, GL_REPEAT, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           RGBA_8888);
-  this->resource_provider_->SetPixels(
-      resource,
-      reinterpret_cast<uint8_t*>(pixels),
-      texture_rect,
-      texture_rect,
-      gfx::Vector2d());
+  this->resource_provider_->CopyToResource(
+      resource, reinterpret_cast<uint8_t*>(pixels), texture_size);
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   TextureDrawQuad* texture_quad =
       pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   texture_quad->SetNew(
-      shared_state,
-      gfx::Rect(this->device_viewport_size_),
-      gfx::Rect(),
-      gfx::Rect(this->device_viewport_size_),
-      resource,
+      shared_state, gfx::Rect(this->device_viewport_size_), gfx::Rect(),
+      gfx::Rect(this->device_viewport_size_), resource,
       true,                     // premultiplied_alpha
       gfx::PointF(0.0f, 0.0f),  // uv_top_left
       gfx::PointF(              // uv_bottom_right
-          this->device_viewport_size_.width() / texture_rect.width(),
-          this->device_viewport_size_.height() / texture_rect.height()),
-      SK_ColorWHITE,
-      vertex_opacity,
+          this->device_viewport_size_.width() / texture_size.width(),
+          this->device_viewport_size_.height() / texture_size.height()),
+      SK_ColorWHITE, vertex_opacity,
       false,   // flipped
       false);  // nearest_neighbor
 
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index 0a33e53..1891546 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -52,7 +52,7 @@
     TexCoordPrecision requested_precision,
     std::string shader_string) {
   switch (requested_precision) {
-    case TexCoordPrecisionHigh:
+    case TEX_COORD_PRECISION_HIGH:
       DCHECK_NE(shader_string.find("TexCoordPrecision"), std::string::npos);
       return "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
              "  #define TexCoordPrecision highp\n"
@@ -60,10 +60,10 @@
              "  #define TexCoordPrecision mediump\n"
              "#endif\n" +
              shader_string;
-    case TexCoordPrecisionMedium:
+    case TEX_COORD_PRECISION_MEDIUM:
       DCHECK_NE(shader_string.find("TexCoordPrecision"), std::string::npos);
       return "#define TexCoordPrecision mediump\n" + shader_string;
-    case TexCoordPrecisionNA:
+    case TEX_COORD_PRECISION_NA:
       DCHECK_EQ(shader_string.find("TexCoordPrecision"), std::string::npos);
       DCHECK_EQ(shader_string.find("texture2D"), std::string::npos);
       DCHECK_EQ(shader_string.find("texture2DRect"), std::string::npos);
@@ -104,34 +104,34 @@
 
   int highp_threshold = std::max(*highp_threshold_cache, highp_threshold_min);
   if (x > highp_threshold || y > highp_threshold)
-    return TexCoordPrecisionHigh;
-  return TexCoordPrecisionMedium;
+    return TEX_COORD_PRECISION_HIGH;
+  return TEX_COORD_PRECISION_MEDIUM;
 }
 
 static std::string SetFragmentSamplerType(SamplerType requested_type,
                                           std::string shader_string) {
   switch (requested_type) {
-    case SamplerType2D:
+    case SAMPLER_TYPE_2D:
       DCHECK_NE(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_NE(shader_string.find("TextureLookup"), std::string::npos);
       return "#define SamplerType sampler2D\n"
              "#define TextureLookup texture2D\n" +
              shader_string;
-    case SamplerType2DRect:
+    case SAMPLER_TYPE_2D_RECT:
       DCHECK_NE(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_NE(shader_string.find("TextureLookup"), std::string::npos);
       return "#extension GL_ARB_texture_rectangle : require\n"
              "#define SamplerType sampler2DRect\n"
              "#define TextureLookup texture2DRect\n" +
              shader_string;
-    case SamplerTypeExternalOES:
+    case SAMPLER_TYPE_EXTERNAL_OES:
       DCHECK_NE(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_NE(shader_string.find("TextureLookup"), std::string::npos);
       return "#extension GL_OES_EGL_image_external : require\n"
              "#define SamplerType samplerExternalOES\n"
              "#define TextureLookup texture2D\n" +
              shader_string;
-    case SamplerTypeNA:
+    case SAMPLER_TYPE_NA:
       DCHECK_EQ(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_EQ(shader_string.find("TextureLookup"), std::string::npos);
       return shader_string;
@@ -734,7 +734,7 @@
 FragmentTexBlendMode::FragmentTexBlendMode()
     : backdrop_location_(-1),
       backdrop_rect_location_(-1),
-      blend_mode_(BlendModeNone) {
+      blend_mode_(BLEND_MODE_NONE) {
 }
 
 std::string FragmentTexBlendMode::SetBlendModeFunctions(
@@ -908,20 +908,20 @@
   });
 
   switch (blend_mode_) {
-    case BlendModeOverlay:
-    case BlendModeHardLight:
+    case BLEND_MODE_OVERLAY:
+    case BLEND_MODE_HARD_LIGHT:
       return kFunctionHardLight;
-    case BlendModeColorDodge:
+    case BLEND_MODE_COLOR_DODGE:
       return kFunctionColorDodgeComponent;
-    case BlendModeColorBurn:
+    case BLEND_MODE_COLOR_BURN:
       return kFunctionColorBurnComponent;
-    case BlendModeSoftLight:
+    case BLEND_MODE_SOFT_LIGHT:
       return kFunctionSoftLightComponentPosDstAlpha;
-    case BlendModeHue:
-    case BlendModeSaturation:
+    case BLEND_MODE_HUE:
+    case BLEND_MODE_SATURATION:
       return kFunctionLum + kFunctionSat;
-    case BlendModeColor:
-    case BlendModeLuminosity:
+    case BLEND_MODE_COLOR:
+    case BLEND_MODE_LUMINOSITY:
       return kFunctionLum;
     default:
       return std::string();
@@ -939,29 +939,29 @@
 
 std::string FragmentTexBlendMode::GetBlendFunctionBodyForRGB() const {
   switch (blend_mode_) {
-    case BlendModeNormal:
+    case BLEND_MODE_NORMAL:
       return "result.rgb = src.rgb + dst.rgb * (1.0 - src.a);";
-    case BlendModeScreen:
+    case BLEND_MODE_SCREEN:
       return "result.rgb = src.rgb + (1.0 - src.rgb) * dst.rgb;";
-    case BlendModeLighten:
+    case BLEND_MODE_LIGHTEN:
       return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb,"
              "                 (1.0 - dst.a) * src.rgb + dst.rgb);";
-    case BlendModeOverlay:
+    case BLEND_MODE_OVERLAY:
       return "result.rgb = hardLight(dst, src);";
-    case BlendModeDarken:
+    case BLEND_MODE_DARKEN:
       return "result.rgb = min((1.0 - src.a) * dst.rgb + src.rgb,"
              "                 (1.0 - dst.a) * src.rgb + dst.rgb);";
-    case BlendModeColorDodge:
+    case BLEND_MODE_COLOR_DODGE:
       return "result.r = getColorDodgeComponent(src.r, src.a, dst.r, dst.a);"
              "result.g = getColorDodgeComponent(src.g, src.a, dst.g, dst.a);"
              "result.b = getColorDodgeComponent(src.b, src.a, dst.b, dst.a);";
-    case BlendModeColorBurn:
+    case BLEND_MODE_COLOR_BURN:
       return "result.r = getColorBurnComponent(src.r, src.a, dst.r, dst.a);"
              "result.g = getColorBurnComponent(src.g, src.a, dst.g, dst.a);"
              "result.b = getColorBurnComponent(src.b, src.a, dst.b, dst.a);";
-    case BlendModeHardLight:
+    case BLEND_MODE_HARD_LIGHT:
       return "result.rgb = hardLight(src, dst);";
-    case BlendModeSoftLight:
+    case BLEND_MODE_SOFT_LIGHT:
       return "if (0.0 == dst.a) {"
              "  result.rgb = src.rgb;"
              "} else {"
@@ -969,15 +969,15 @@
              "  result.g = getSoftLightComponent(src.g, src.a, dst.g, dst.a);"
              "  result.b = getSoftLightComponent(src.b, src.a, dst.b, dst.a);"
              "}";
-    case BlendModeDifference:
+    case BLEND_MODE_DIFFERENCE:
       return "result.rgb = src.rgb + dst.rgb -"
              "    2.0 * min(src.rgb * dst.a, dst.rgb * src.a);";
-    case BlendModeExclusion:
+    case BLEND_MODE_EXCLUSION:
       return "result.rgb = dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb;";
-    case BlendModeMultiply:
+    case BLEND_MODE_MULTIPLY:
       return "result.rgb = (1.0 - src.a) * dst.rgb +"
              "    (1.0 - dst.a) * src.rgb + src.rgb * dst.rgb;";
-    case BlendModeHue:
+    case BLEND_MODE_HUE:
       return "vec4 dstSrcAlpha = dst * src.a;"
              "result.rgb ="
              "    set_luminance(set_saturation(src.rgb * dst.a,"
@@ -985,27 +985,26 @@
              "                  dstSrcAlpha.a,"
              "                  dstSrcAlpha.rgb);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeSaturation:
+    case BLEND_MODE_SATURATION:
       return "vec4 dstSrcAlpha = dst * src.a;"
              "result.rgb = set_luminance(set_saturation(dstSrcAlpha.rgb,"
              "                                          src.rgb * dst.a),"
              "                           dstSrcAlpha.a,"
              "                           dstSrcAlpha.rgb);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeColor:
+    case BLEND_MODE_COLOR:
       return "vec4 srcDstAlpha = src * dst.a;"
              "result.rgb = set_luminance(srcDstAlpha.rgb,"
              "                           srcDstAlpha.a,"
              "                           dst.rgb * src.a);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeLuminosity:
+    case BLEND_MODE_LUMINOSITY:
       return "vec4 srcDstAlpha = src * dst.a;"
              "result.rgb = set_luminance(dst.rgb * src.a,"
              "                           srcDstAlpha.a,"
              "                           srcDstAlpha.rgb);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeNone:
-    case NumBlendModes:
+    case BLEND_MODE_NONE:
       NOTREACHED();
   }
   return "result = vec4(1.0, 0.0, 0.0, 1.0);";
diff --git a/cc/output/shader.h b/cc/output/shader.h
index 3c8102e..93407d4 100644
--- a/cc/output/shader.h
+++ b/cc/output/shader.h
@@ -24,39 +24,39 @@
 namespace cc {
 
 enum TexCoordPrecision {
-  TexCoordPrecisionNA = 0,
-  TexCoordPrecisionMedium = 1,
-  TexCoordPrecisionHigh = 2,
-  NumTexCoordPrecisions = 3
+  TEX_COORD_PRECISION_NA = 0,
+  TEX_COORD_PRECISION_MEDIUM = 1,
+  TEX_COORD_PRECISION_HIGH = 2,
+  LAST_TEX_COORD_PRECISION = 2
 };
 
 enum SamplerType {
-  SamplerTypeNA = 0,
-  SamplerType2D = 1,
-  SamplerType2DRect = 2,
-  SamplerTypeExternalOES = 3,
-  NumSamplerTypes = 4
+  SAMPLER_TYPE_NA = 0,
+  SAMPLER_TYPE_2D = 1,
+  SAMPLER_TYPE_2D_RECT = 2,
+  SAMPLER_TYPE_EXTERNAL_OES = 3,
+  LAST_SAMPLER_TYPE = 3
 };
 
 enum BlendMode {
-  BlendModeNone,
-  BlendModeNormal,
-  BlendModeScreen,
-  BlendModeOverlay,
-  BlendModeDarken,
-  BlendModeLighten,
-  BlendModeColorDodge,
-  BlendModeColorBurn,
-  BlendModeHardLight,
-  BlendModeSoftLight,
-  BlendModeDifference,
-  BlendModeExclusion,
-  BlendModeMultiply,
-  BlendModeHue,
-  BlendModeSaturation,
-  BlendModeColor,
-  BlendModeLuminosity,
-  NumBlendModes
+  BLEND_MODE_NONE,
+  BLEND_MODE_NORMAL,
+  BLEND_MODE_SCREEN,
+  BLEND_MODE_OVERLAY,
+  BLEND_MODE_DARKEN,
+  BLEND_MODE_LIGHTEN,
+  BLEND_MODE_COLOR_DODGE,
+  BLEND_MODE_COLOR_BURN,
+  BLEND_MODE_HARD_LIGHT,
+  BLEND_MODE_SOFT_LIGHT,
+  BLEND_MODE_DIFFERENCE,
+  BLEND_MODE_EXCLUSION,
+  BLEND_MODE_MULTIPLY,
+  BLEND_MODE_HUE,
+  BLEND_MODE_SATURATION,
+  BLEND_MODE_COLOR,
+  BLEND_MODE_LUMINOSITY,
+  LAST_BLEND_MODE = BLEND_MODE_LUMINOSITY
 };
 
 // Note: The highp_threshold_cache must be provided by the caller to make
@@ -329,7 +329,7 @@
 
   BlendMode blend_mode() const { return blend_mode_; }
   void set_blend_mode(BlendMode blend_mode) { blend_mode_ = blend_mode; }
-  bool has_blend_mode() const { return blend_mode_ != BlendModeNone; }
+  bool has_blend_mode() const { return blend_mode_ != BLEND_MODE_NONE; }
 
  protected:
   FragmentTexBlendMode();
diff --git a/cc/output/shader_unittest.cc b/cc/output/shader_unittest.cc
index 4fd696f..675e47f 100644
--- a/cc/output/shader_unittest.cc
+++ b/cc/output/shader_unittest.cc
@@ -27,24 +27,32 @@
   gfx::Size bigSize(2560, 2560);
 
   threshold_min = 0;
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, closePoint));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, smallSize));
-  EXPECT_EQ(TexCoordPrecisionHigh, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, farPoint));
-  EXPECT_EQ(TexCoordPrecisionHigh, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, bigSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      closePoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      smallSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_HIGH,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      farPoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_HIGH,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      bigSize));
 
   threshold_min = 3000;
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, closePoint));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, smallSize));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, farPoint));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, bigSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      closePoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      smallSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      farPoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      bigSize));
 }
 
 }  // namespace cc
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index 488ca09..5360d59 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -223,11 +223,11 @@
 bool SoftwareRenderer::IsSoftwareResource(
     ResourceProvider::ResourceId resource_id) const {
   switch (resource_provider_->GetResourceType(resource_id)) {
-    case ResourceProvider::GLTexture:
+    case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE:
       return false;
-    case ResourceProvider::Bitmap:
+    case ResourceProvider::RESOURCE_TYPE_BITMAP:
       return true;
-    case ResourceProvider::InvalidType:
+    case ResourceProvider::RESOURCE_TYPE_INVALID:
       break;
   }
 
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc
index fa25200..3b2c4bc 100644
--- a/cc/output/software_renderer_unittest.cc
+++ b/cc/output/software_renderer_unittest.cc
@@ -155,16 +155,12 @@
 
   ResourceProvider::ResourceId resource_yellow =
       resource_provider()->CreateResource(
-          outer_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          RGBA_8888);
+          outer_size, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   ResourceProvider::ResourceId resource_cyan =
       resource_provider()->CreateResource(
-          inner_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          RGBA_8888);
+          inner_size, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
 
   SkBitmap yellow_tile;
   yellow_tile.allocN32Pixels(outer_size.width(), outer_size.height());
@@ -174,17 +170,11 @@
   cyan_tile.allocN32Pixels(inner_size.width(), inner_size.height());
   cyan_tile.eraseColor(SK_ColorCYAN);
 
-  resource_provider()->SetPixels(
-      resource_yellow,
-      static_cast<uint8_t*>(yellow_tile.getPixels()),
-      gfx::Rect(outer_size),
-      gfx::Rect(outer_size),
-      gfx::Vector2d());
-  resource_provider()->SetPixels(resource_cyan,
-                                 static_cast<uint8_t*>(cyan_tile.getPixels()),
-                                 gfx::Rect(inner_size),
-                                 gfx::Rect(inner_size),
-                                 gfx::Vector2d());
+  resource_provider()->CopyToResource(
+      resource_yellow, static_cast<uint8_t*>(yellow_tile.getPixels()),
+      outer_size);
+  resource_provider()->CopyToResource(
+      resource_cyan, static_cast<uint8_t*>(cyan_tile.getPixels()), inner_size);
 
   gfx::Rect root_rect = outer_rect;
 
@@ -252,9 +242,7 @@
 
   ResourceProvider::ResourceId resource_cyan =
       resource_provider()->CreateResource(
-          tile_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          tile_size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           RGBA_8888);
 
   SkBitmap cyan_tile;  // The lowest five rows are yellow.
@@ -265,11 +253,8 @@
           0, visible_rect.bottom() - 1, tile_rect.width(), tile_rect.bottom()),
       SK_ColorYELLOW);
 
-  resource_provider()->SetPixels(resource_cyan,
-                                 static_cast<uint8_t*>(cyan_tile.getPixels()),
-                                 gfx::Rect(tile_size),
-                                 gfx::Rect(tile_size),
-                                 gfx::Vector2d());
+  resource_provider()->CopyToResource(
+      resource_cyan, static_cast<uint8_t*>(cyan_tile.getPixels()), tile_size);
 
   gfx::Rect root_rect(tile_size);
 
diff --git a/cc/quads/draw_polygon.cc b/cc/quads/draw_polygon.cc
index 71dbf36..026be2c 100644
--- a/cc/quads/draw_polygon.cc
+++ b/cc/quads/draw_polygon.cc
@@ -93,7 +93,7 @@
 
 // Checks whether or not shape a lies on the front or back side of b, or
 // whether they should be considered coplanar. If on the back side, we
-// say ABeforeB because it should be drawn in that order.
+// say A_BEFORE_B because it should be drawn in that order.
 // Assumes that layers are split and there are no intersecting planes.
 BspCompareResult DrawPolygon::SideCompare(const DrawPolygon& a,
                                           const DrawPolygon& b) {
diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc
index 6b51398..b7c1bdf 100644
--- a/cc/quads/draw_quad_unittest.cc
+++ b/cc/quads/draw_quad_unittest.cc
@@ -643,7 +643,7 @@
   ResourceProvider::ResourceId u_plane_resource_id = 532;
   ResourceProvider::ResourceId v_plane_resource_id = 4;
   ResourceProvider::ResourceId a_plane_resource_id = 63;
-  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::REC_601_JPEG;
+  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::JPEG;
   CREATE_SHARED_STATE();
 
   CREATE_QUAD_9_NEW(YUVVideoDrawQuad, opaque_rect, visible_rect, tex_coord_rect,
@@ -899,7 +899,7 @@
   ResourceProvider::ResourceId u_plane_resource_id = 532;
   ResourceProvider::ResourceId v_plane_resource_id = 4;
   ResourceProvider::ResourceId a_plane_resource_id = 63;
-  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::REC_601_JPEG;
+  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::JPEG;
 
   CREATE_SHARED_STATE();
   CREATE_QUAD_9_NEW(YUVVideoDrawQuad, opaque_rect, visible_rect, tex_coord_rect,
diff --git a/cc/quads/yuv_video_draw_quad.h b/cc/quads/yuv_video_draw_quad.h
index d57d56f..15bce98 100644
--- a/cc/quads/yuv_video_draw_quad.h
+++ b/cc/quads/yuv_video_draw_quad.h
@@ -15,9 +15,10 @@
 class CC_EXPORT YUVVideoDrawQuad : public DrawQuad {
  public:
   enum ColorSpace {
-    REC_601,       // SDTV standard with restricted "studio swing" color range.
-    REC_601_JPEG,  // Full color range [0, 255] variant of the above.
-    COLOR_SPACE_LAST = REC_601_JPEG
+    REC_601,  // SDTV standard with restricted "studio swing" color range.
+    REC_709,  // HDTV standard with restricted "studio swing" color range.
+    JPEG,     // Full color range [0, 255] JPEG color space.
+    COLOR_SPACE_LAST = JPEG
   };
 
   ~YUVVideoDrawQuad() override;
diff --git a/cc/resources/display_item_list_unittest.cc b/cc/resources/display_item_list_unittest.cc
index ddf2cb7..c9b9558 100644
--- a/cc/resources/display_item_list_unittest.cc
+++ b/cc/resources/display_item_list_unittest.cc
@@ -37,13 +37,15 @@
   unsigned char pixels[4 * 100 * 100] = {0};
   scoped_refptr<DisplayItemList> list = DisplayItemList::Create();
 
+  gfx::PointF offset(8.f, 9.f);
+  gfx::RectF recording_rect(offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(recording_rect)));
+  canvas->translate(offset.x(), offset.y());
   canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
   canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF offset(8.f, 9.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
   DrawDisplayList(pixels, layer_rect, list);
 
   SkBitmap expected_bitmap;
@@ -75,22 +77,26 @@
   unsigned char pixels[4 * 100 * 100] = {0};
   scoped_refptr<DisplayItemList> list = DisplayItemList::Create();
 
+  gfx::PointF first_offset(8.f, 9.f);
+  gfx::RectF first_recording_rect(first_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect)));
+  canvas->translate(first_offset.x(), first_offset.y());
   canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF first_offset(8.f, 9.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, first_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   gfx::Rect clip_rect(60, 60, 10, 10);
   list->AppendItem(ClipDisplayItem::Create(clip_rect, std::vector<SkRRect>()));
 
+  gfx::PointF second_offset(2.f, 3.f);
+  gfx::RectF second_recording_rect(second_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect)));
+  canvas->translate(second_offset.x(), second_offset.y());
   canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF second_offset(2.f, 3.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, second_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   list->AppendItem(EndClipDisplayItem::Create());
 
@@ -126,23 +132,27 @@
   unsigned char pixels[4 * 100 * 100] = {0};
   scoped_refptr<DisplayItemList> list = DisplayItemList::Create();
 
+  gfx::PointF first_offset(8.f, 9.f);
+  gfx::RectF first_recording_rect(first_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect)));
+  canvas->translate(first_offset.x(), first_offset.y());
   canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF first_offset(8.f, 9.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, first_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   gfx::Transform transform;
   transform.Rotate(45.0);
   list->AppendItem(TransformDisplayItem::Create(transform));
 
+  gfx::PointF second_offset(2.f, 3.f);
+  gfx::RectF second_recording_rect(second_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect)));
+  canvas->translate(second_offset.x(), second_offset.y());
   canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF second_offset(2.f, 3.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, second_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   list->AppendItem(EndTransformDisplayItem::Create());
 
diff --git a/cc/resources/display_list_raster_source.cc b/cc/resources/display_list_raster_source.cc
index 4479a16..ab0292e 100644
--- a/cc/resources/display_list_raster_source.cc
+++ b/cc/resources/display_list_raster_source.cc
@@ -26,14 +26,12 @@
 
 namespace cc {
 
-scoped_refptr<DisplayListRasterSource> DisplayListRasterSource::Create() {
-  return make_scoped_refptr(new DisplayListRasterSource);
-}
-
 scoped_refptr<DisplayListRasterSource>
 DisplayListRasterSource::CreateFromDisplayListRecordingSource(
-    const DisplayListRecordingSource* other) {
-  return make_scoped_refptr(new DisplayListRasterSource(other));
+    const DisplayListRecordingSource* other,
+    bool can_use_lcd_text) {
+  return make_scoped_refptr(
+      new DisplayListRasterSource(other, can_use_lcd_text));
 }
 
 DisplayListRasterSource::DisplayListRasterSource()
@@ -48,11 +46,12 @@
 }
 
 DisplayListRasterSource::DisplayListRasterSource(
-    const DisplayListRecordingSource* other)
+    const DisplayListRecordingSource* other,
+    bool can_use_lcd_text)
     : display_list_(other->display_list_),
       background_color_(other->background_color_),
       requires_clear_(other->requires_clear_),
-      can_use_lcd_text_(other->can_use_lcd_text_),
+      can_use_lcd_text_(can_use_lcd_text),
       is_solid_color_(other->is_solid_color_),
       solid_color_(other->solid_color_),
       recorded_viewport_(other->recorded_viewport_),
@@ -63,6 +62,24 @@
       should_attempt_to_use_distance_field_text_(false) {
 }
 
+DisplayListRasterSource::DisplayListRasterSource(
+    const DisplayListRasterSource* other,
+    bool can_use_lcd_text)
+    : display_list_(other->display_list_),
+      background_color_(other->background_color_),
+      requires_clear_(other->requires_clear_),
+      can_use_lcd_text_(can_use_lcd_text),
+      is_solid_color_(other->is_solid_color_),
+      solid_color_(other->solid_color_),
+      recorded_viewport_(other->recorded_viewport_),
+      size_(other->size_),
+      clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
+      slow_down_raster_scale_factor_for_debug_(
+          other->slow_down_raster_scale_factor_for_debug_),
+      should_attempt_to_use_distance_field_text_(
+          other->should_attempt_to_use_distance_field_text_) {
+}
+
 DisplayListRasterSource::~DisplayListRasterSource() {
 }
 
@@ -197,4 +214,11 @@
   return can_use_lcd_text_;
 }
 
+scoped_refptr<RasterSource> DisplayListRasterSource::CreateCloneWithoutLCDText()
+    const {
+  bool can_use_lcd_text = false;
+  return scoped_refptr<RasterSource>(
+      new DisplayListRasterSource(this, can_use_lcd_text));
+}
+
 }  // namespace cc
diff --git a/cc/resources/display_list_raster_source.h b/cc/resources/display_list_raster_source.h
index f095231..44177fb 100644
--- a/cc/resources/display_list_raster_source.h
+++ b/cc/resources/display_list_raster_source.h
@@ -21,9 +21,9 @@
 
 class CC_EXPORT DisplayListRasterSource : public RasterSource {
  public:
-  static scoped_refptr<DisplayListRasterSource> Create();
   static scoped_refptr<DisplayListRasterSource>
-  CreateFromDisplayListRecordingSource(const DisplayListRecordingSource* other);
+  CreateFromDisplayListRecordingSource(const DisplayListRecordingSource* other,
+                                       bool can_use_lcd_text);
 
   // RasterSource overrides.
   void PlaybackToCanvas(SkCanvas* canvas,
@@ -52,10 +52,14 @@
   skia::RefPtr<SkPicture> GetFlattenedPicture() override;
   size_t GetPictureMemoryUsage() const override;
   bool CanUseLCDText() const override;
+  scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const override;
 
  protected:
   DisplayListRasterSource();
-  explicit DisplayListRasterSource(const DisplayListRecordingSource* other);
+  DisplayListRasterSource(const DisplayListRecordingSource* other,
+                          bool can_use_lcd_text);
+  DisplayListRasterSource(const DisplayListRasterSource* other,
+                          bool can_use_lcd_text);
   ~DisplayListRasterSource() override;
 
   // These members are const as this raster source may be in use on another
diff --git a/cc/resources/display_list_recording_source.cc b/cc/resources/display_list_recording_source.cc
index 01a3bce..517f49f 100644
--- a/cc/resources/display_list_recording_source.cc
+++ b/cc/resources/display_list_recording_source.cc
@@ -28,7 +28,6 @@
 
 DisplayListRecordingSource::DisplayListRecordingSource()
     : slow_down_raster_scale_factor_for_debug_(0),
-      can_use_lcd_text_(true),
       requires_clear_(false),
       is_solid_color_(false),
       solid_color_(SK_ColorTRANSPARENT),
@@ -43,7 +42,6 @@
 bool DisplayListRecordingSource::UpdateAndExpandInvalidation(
     ContentLayerClient* painter,
     Region* invalidation,
-    bool can_use_lcd_text,
     const gfx::Size& layer_size,
     const gfx::Rect& visible_layer_rect,
     int frame_number,
@@ -55,12 +53,6 @@
     updated = true;
   }
 
-  if (can_use_lcd_text_ != can_use_lcd_text) {
-    can_use_lcd_text_ = can_use_lcd_text;
-    invalidation->Union(gfx::Rect(GetSize()));
-    updated = true;
-  }
-
   gfx::Rect old_recorded_viewport = recorded_viewport_;
   recorded_viewport_ = visible_layer_rect;
   recorded_viewport_.Inset(-pixel_record_distance_, -pixel_record_distance_);
@@ -152,10 +144,11 @@
   return is_suitable_for_gpu_rasterization_;
 }
 
-scoped_refptr<RasterSource> DisplayListRecordingSource::CreateRasterSource()
-    const {
+scoped_refptr<RasterSource> DisplayListRecordingSource::CreateRasterSource(
+    bool can_use_lcd_text) const {
   return scoped_refptr<RasterSource>(
-      DisplayListRasterSource::CreateFromDisplayListRecordingSource(this));
+      DisplayListRasterSource::CreateFromDisplayListRecordingSource(
+          this, can_use_lcd_text));
 }
 
 gfx::Size DisplayListRecordingSource::GetTileGridSizeForTesting() const {
diff --git a/cc/resources/display_list_recording_source.h b/cc/resources/display_list_recording_source.h
index 21a24fb..1e713e1 100644
--- a/cc/resources/display_list_recording_source.h
+++ b/cc/resources/display_list_recording_source.h
@@ -20,12 +20,12 @@
   // RecordingSource overrides.
   bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                    Region* invalidation,
-                                   bool can_use_lcd_text,
                                    const gfx::Size& layer_size,
                                    const gfx::Rect& visible_layer_rect,
                                    int frame_number,
                                    RecordingMode recording_mode) override;
-  scoped_refptr<RasterSource> CreateRasterSource() const override;
+  scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const override;
   gfx::Size GetSize() const final;
   void SetEmptyBounds() override;
   void SetSlowdownRasterScaleFactor(int factor) override;
@@ -41,7 +41,6 @@
   gfx::Rect recorded_viewport_;
   gfx::Size size_;
   int slow_down_raster_scale_factor_for_debug_;
-  bool can_use_lcd_text_;
   bool requires_clear_;
   bool is_solid_color_;
   SkColor solid_color_;
diff --git a/cc/resources/drawing_display_item.cc b/cc/resources/drawing_display_item.cc
index 1ef149f..6dffad9 100644
--- a/cc/resources/drawing_display_item.cc
+++ b/cc/resources/drawing_display_item.cc
@@ -18,9 +18,8 @@
 
 namespace cc {
 
-DrawingDisplayItem::DrawingDisplayItem(skia::RefPtr<SkPicture> picture,
-                                       gfx::PointF location)
-    : picture_(picture), location_(location) {
+DrawingDisplayItem::DrawingDisplayItem(skia::RefPtr<SkPicture> picture)
+    : picture_(picture) {
 }
 
 DrawingDisplayItem::~DrawingDisplayItem() {
@@ -29,7 +28,6 @@
 void DrawingDisplayItem::Raster(SkCanvas* canvas,
                                 SkDrawPictureCallback* callback) const {
   canvas->save();
-  canvas->translate(location_.x(), location_.y());
   if (callback)
     picture_->playback(canvas, callback);
   else
@@ -39,7 +37,6 @@
 
 void DrawingDisplayItem::RasterForTracing(SkCanvas* canvas) const {
   canvas->save();
-  canvas->translate(location_.x(), location_.y());
   // The picture debugger in about:tracing doesn't drill down into |drawPicture|
   // operations. Calling |playback()| rather than |drawPicture()| causes the
   // skia operations in |picture_| to appear individually in the picture
@@ -54,7 +51,7 @@
 }
 
 int DrawingDisplayItem::ApproximateOpCount() const {
-  return picture_->approximateOpCount() + sizeof(gfx::PointF);
+  return picture_->approximateOpCount();
 }
 
 size_t DrawingDisplayItem::PictureMemoryUsage() const {
@@ -66,9 +63,11 @@
     base::trace_event::TracedValue* array) const {
   array->BeginDictionary();
   array->SetString("name", "DrawingDisplayItem");
-  array->SetString("location",
-                   base::StringPrintf("[%f,%f]", picture_->cullRect().x(),
-                                      picture_->cullRect().y()));
+  array->SetString(
+      "cullRect",
+      base::StringPrintf("[%f,%f,%f,%f]", picture_->cullRect().x(),
+                         picture_->cullRect().y(), picture_->cullRect().width(),
+                         picture_->cullRect().height()));
   std::string b64_picture;
   PictureDebugUtil::SerializeAsBase64(picture_.get(), &b64_picture);
   array->SetString("skp64", b64_picture);
diff --git a/cc/resources/drawing_display_item.h b/cc/resources/drawing_display_item.h
index 1f63ed3..b45a039 100644
--- a/cc/resources/drawing_display_item.h
+++ b/cc/resources/drawing_display_item.h
@@ -21,9 +21,9 @@
  public:
   ~DrawingDisplayItem() override;
 
-  static scoped_ptr<DrawingDisplayItem> Create(skia::RefPtr<SkPicture> picture,
-                                               gfx::PointF location) {
-    return make_scoped_ptr(new DrawingDisplayItem(picture, location));
+  static scoped_ptr<DrawingDisplayItem> Create(
+      skia::RefPtr<SkPicture> picture) {
+    return make_scoped_ptr(new DrawingDisplayItem(picture));
   }
 
   void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
@@ -35,11 +35,10 @@
   void AsValueInto(base::trace_event::TracedValue* array) const override;
 
  protected:
-  DrawingDisplayItem(skia::RefPtr<SkPicture> picture, gfx::PointF location);
+  explicit DrawingDisplayItem(skia::RefPtr<SkPicture> picture);
 
  private:
   skia::RefPtr<SkPicture> picture_;
-  gfx::PointF location_;
 };
 
 }  // namespace cc
diff --git a/cc/resources/gpu_rasterizer.cc b/cc/resources/gpu_rasterizer.cc
index e0097f8..6e318ed 100644
--- a/cc/resources/gpu_rasterizer.cc
+++ b/cc/resources/gpu_rasterizer.cc
@@ -123,7 +123,7 @@
     SkMultiPictureDraw multi_picture_draw;
     multi_picture_draw.add(write_lock->sk_surface()->getCanvas(),
                            picture.get());
-    multi_picture_draw.draw(false);
+    multi_picture_draw.draw(msaa_sample_count_ > 0);
     write_lock->ReleaseSkSurface();
   }
 }
diff --git a/cc/resources/one_copy_tile_task_worker_pool.cc b/cc/resources/one_copy_tile_task_worker_pool.cc
index cee568a..e6a367a 100644
--- a/cc/resources/one_copy_tile_task_worker_pool.cc
+++ b/cc/resources/one_copy_tile_task_worker_pool.cc
@@ -167,6 +167,13 @@
 void OneCopyTileTaskWorkerPool::ScheduleTasks(TileTaskQueue* queue) {
   TRACE_EVENT0("cc", "OneCopyTileTaskWorkerPool::ScheduleTasks");
 
+#if DCHECK_IS_ON()
+  {
+    base::AutoLock lock(lock_);
+    DCHECK(!shutdown_);
+  }
+#endif
+
   if (tasks_pending_.none())
     TRACE_EVENT_ASYNC_BEGIN0("cc", "ScheduledTasks", this);
 
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index 7e94e18..2b15aea 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -166,7 +166,6 @@
                          const gfx::Size& tile_grid_size)
     : min_contents_scale_(0),
       slow_down_raster_scale_factor_for_debug_(0),
-      can_use_lcd_text_(true),
       has_any_recordings_(false),
       clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
       requires_clear_(true),
@@ -186,22 +185,17 @@
 bool PicturePile::UpdateAndExpandInvalidation(
     ContentLayerClient* painter,
     Region* invalidation,
-    bool can_use_lcd_text,
     const gfx::Size& layer_size,
     const gfx::Rect& visible_layer_rect,
     int frame_number,
     RecordingSource::RecordingMode recording_mode) {
-  bool can_use_lcd_text_changed = can_use_lcd_text_ != can_use_lcd_text;
-  can_use_lcd_text_ = can_use_lcd_text;
-
   gfx::Rect interest_rect = visible_layer_rect;
   interest_rect.Inset(-pixel_record_distance_, -pixel_record_distance_);
   recorded_viewport_ = interest_rect;
   recorded_viewport_.Intersect(gfx::Rect(layer_size));
 
-  bool updated =
-      ApplyInvalidationAndResize(interest_rect, invalidation, layer_size,
-                                 frame_number, can_use_lcd_text_changed);
+  bool updated = ApplyInvalidationAndResize(interest_rect, invalidation,
+                                            layer_size, frame_number);
   std::vector<gfx::Rect> invalid_tiles;
   GetInvalidTileRects(interest_rect, invalidation, visible_layer_rect,
                       frame_number, &invalid_tiles);
@@ -223,8 +217,7 @@
 bool PicturePile::ApplyInvalidationAndResize(const gfx::Rect& interest_rect,
                                              Region* invalidation,
                                              const gfx::Size& layer_size,
-                                             int frame_number,
-                                             bool can_use_lcd_text_changed) {
+                                             int frame_number) {
   bool updated = false;
 
   Region synthetic_invalidation;
@@ -233,14 +226,6 @@
     tiling_.SetTilingSize(layer_size);
     updated = true;
   }
-  if (can_use_lcd_text_changed) {
-    // When LCD text is enabled/disabled, we must drop any raster tiles for
-    // the pile, so they can be recreated in a manner consistent with the new
-    // setting. We do this with |synthetic_invalidation| since we don't need to
-    // do a new recording, just invalidate rastered content.
-    synthetic_invalidation.Union(gfx::Rect(GetSize()));
-    updated = true;
-  }
 
   gfx::Rect interest_rect_over_tiles =
       tiling_.ExpandRectToTileBounds(interest_rect);
@@ -593,9 +578,10 @@
   }
 }
 
-scoped_refptr<RasterSource> PicturePile::CreateRasterSource() const {
+scoped_refptr<RasterSource> PicturePile::CreateRasterSource(
+    bool can_use_lcd_text) const {
   return scoped_refptr<RasterSource>(
-      PicturePileImpl::CreateFromPicturePile(this));
+      PicturePileImpl::CreateFromPicturePile(this, can_use_lcd_text));
 }
 
 gfx::Size PicturePile::GetSize() const {
diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h
index 2a9a3ce..bab06c8 100644
--- a/cc/resources/picture_pile.h
+++ b/cc/resources/picture_pile.h
@@ -25,12 +25,12 @@
   // RecordingSource overrides.
   bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                    Region* invalidation,
-                                   bool can_use_lcd_text,
                                    const gfx::Size& layer_size,
                                    const gfx::Rect& visible_layer_rect,
                                    int frame_number,
                                    RecordingMode recording_mode) override;
-  scoped_refptr<RasterSource> CreateRasterSource() const override;
+  scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const override;
   gfx::Size GetSize() const final;
   void SetEmptyBounds() override;
   void SetSlowdownRasterScaleFactor(int factor) override;
@@ -95,7 +95,6 @@
   float min_contents_scale_;
   gfx::Size tile_grid_size_;
   int slow_down_raster_scale_factor_for_debug_;
-  bool can_use_lcd_text_;
   // A hint about whether there are any recordings. This may be a false
   // positive.
   bool has_any_recordings_;
@@ -120,8 +119,7 @@
   bool ApplyInvalidationAndResize(const gfx::Rect& interest_rect,
                                   Region* invalidation,
                                   const gfx::Size& layer_size,
-                                  int frame_number,
-                                  bool can_use_lcd_text_changed);
+                                  int frame_number);
   void DetermineIfSolidColor();
   void SetBufferPixels(int buffer_pixels);
 
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc
index d629888..a9bad90 100644
--- a/cc/resources/picture_pile_impl.cc
+++ b/cc/resources/picture_pile_impl.cc
@@ -19,14 +19,15 @@
 namespace cc {
 
 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromPicturePile(
-    const PicturePile* other) {
-  return make_scoped_refptr(new PicturePileImpl(other));
+    const PicturePile* other,
+    bool can_use_lcd_text) {
+  return make_scoped_refptr(new PicturePileImpl(other, can_use_lcd_text));
 }
 
 PicturePileImpl::PicturePileImpl()
     : background_color_(SK_ColorTRANSPARENT),
       requires_clear_(true),
-      can_use_lcd_text_(false),
+      can_use_lcd_text_(true),
       is_solid_color_(false),
       solid_color_(SK_ColorTRANSPARENT),
       has_any_recordings_(false),
@@ -36,12 +37,13 @@
       should_attempt_to_use_distance_field_text_(false) {
 }
 
-PicturePileImpl::PicturePileImpl(const PicturePile* other)
+PicturePileImpl::PicturePileImpl(const PicturePile* other,
+                                 bool can_use_lcd_text)
     : picture_map_(other->picture_map_),
       tiling_(other->tiling_),
       background_color_(other->background_color_),
       requires_clear_(other->requires_clear_),
-      can_use_lcd_text_(other->can_use_lcd_text_),
+      can_use_lcd_text_(can_use_lcd_text),
       is_solid_color_(other->is_solid_color_),
       solid_color_(other->solid_color_),
       recorded_viewport_(other->recorded_viewport_),
@@ -53,6 +55,25 @@
       should_attempt_to_use_distance_field_text_(false) {
 }
 
+PicturePileImpl::PicturePileImpl(const PicturePileImpl* other,
+                                 bool can_use_lcd_text)
+    : picture_map_(other->picture_map_),
+      tiling_(other->tiling_),
+      background_color_(other->background_color_),
+      requires_clear_(other->requires_clear_),
+      can_use_lcd_text_(can_use_lcd_text),
+      is_solid_color_(other->is_solid_color_),
+      solid_color_(other->solid_color_),
+      recorded_viewport_(other->recorded_viewport_),
+      has_any_recordings_(other->has_any_recordings_),
+      clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
+      min_contents_scale_(other->min_contents_scale_),
+      slow_down_raster_scale_factor_for_debug_(
+          other->slow_down_raster_scale_factor_for_debug_),
+      should_attempt_to_use_distance_field_text_(
+          other->should_attempt_to_use_distance_field_text_) {
+}
+
 PicturePileImpl::~PicturePileImpl() {
 }
 
@@ -383,6 +404,13 @@
   return can_use_lcd_text_;
 }
 
+scoped_refptr<RasterSource> PicturePileImpl::CreateCloneWithoutLCDText() const {
+  DCHECK(CanUseLCDText());
+  bool can_use_lcd_text = false;
+  return scoped_refptr<RasterSource>(
+      new PicturePileImpl(this, can_use_lcd_text));
+}
+
 PicturePileImpl::PixelRefIterator::PixelRefIterator(
     const gfx::Rect& content_rect,
     float contents_scale,
diff --git a/cc/resources/picture_pile_impl.h b/cc/resources/picture_pile_impl.h
index 1f94cec..c35dd0f 100644
--- a/cc/resources/picture_pile_impl.h
+++ b/cc/resources/picture_pile_impl.h
@@ -30,7 +30,8 @@
 class CC_EXPORT PicturePileImpl : public RasterSource {
  public:
   static scoped_refptr<PicturePileImpl> CreateFromPicturePile(
-      const PicturePile* other);
+      const PicturePile* other,
+      bool can_use_lcd_text);
 
   // RasterSource overrides. See RasterSource header for full description.
   // When slow-down-raster-scale-factor is set to a value greater than 1, the
@@ -58,6 +59,7 @@
   SkColor GetSolidColor() const override;
   bool HasRecordings() const override;
   bool CanUseLCDText() const override;
+  scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const override;
 
   // Tracing functionality.
   void DidBeginTracing() override;
@@ -99,7 +101,8 @@
   using PictureInfo = PicturePile::PictureInfo;
 
   PicturePileImpl();
-  explicit PicturePileImpl(const PicturePile* other);
+  explicit PicturePileImpl(const PicturePile* other, bool can_use_lcd_text);
+  explicit PicturePileImpl(const PicturePileImpl* other, bool can_use_lcd_text);
   ~PicturePileImpl() override;
 
   int buffer_pixels() const { return tiling_.border_texels(); }
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc
index b834979..91ad7ce 100644
--- a/cc/resources/picture_pile_unittest.cc
+++ b/cc/resources/picture_pile_unittest.cc
@@ -42,9 +42,9 @@
                                    const gfx::Size& layer_size,
                                    const gfx::Rect& visible_layer_rect) {
     frame_number_++;
-    return pile_.UpdateAndExpandInvalidation(
-        &client_, invalidation, false, layer_size, visible_layer_rect,
-        frame_number_, RecordingSource::RECORD_NORMALLY);
+    return pile_.UpdateAndExpandInvalidation(&client_, invalidation, layer_size,
+                                             visible_layer_rect, frame_number_,
+                                             RecordingSource::RECORD_NORMALLY);
   }
 
   bool UpdateWholePile() {
diff --git a/cc/resources/platform_color_unittest.cc b/cc/resources/platform_color_unittest.cc
new file mode 100644
index 0000000..83b2a1e
--- /dev/null
+++ b/cc/resources/platform_color_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/platform_color.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+// Verify SameComponentOrder on this platform.
+TEST(PlatformColorTest, SameComponentOrder) {
+  bool rgba = !!SK_B32_SHIFT;
+
+  for (size_t i = 0; i <= RESOURCE_FORMAT_MAX; ++i) {
+    ResourceFormat format = static_cast<ResourceFormat>(i);
+    switch (format) {
+      case RGBA_8888:
+        EXPECT_EQ(rgba, PlatformColor::SameComponentOrder(format));
+        break;
+      case RGBA_4444:
+        // RGBA_4444 indicates the number of bytes per pixel but the format
+        // doesn't actually imply RGBA ordering. It uses the native ordering.
+        EXPECT_EQ(true, PlatformColor::SameComponentOrder(format));
+        break;
+      case BGRA_8888:
+        EXPECT_NE(rgba, PlatformColor::SameComponentOrder(format));
+        break;
+      case ALPHA_8:
+      case LUMINANCE_8:
+      case RGB_565:
+      case ETC1:
+      case RED_8:
+        EXPECT_FALSE(PlatformColor::SameComponentOrder(format));
+        break;
+    }
+  }
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/resources/prioritized_resource_manager.cc b/cc/resources/prioritized_resource_manager.cc
index 48177e3..236377b 100644
--- a/cc/resources/prioritized_resource_manager.cc
+++ b/cc/resources/prioritized_resource_manager.cc
@@ -451,11 +451,8 @@
   DCHECK(resource_provider);
   ResourceProvider::ResourceId resource_id =
       resource_provider->CreateManagedResource(
-          size,
-          GL_TEXTURE_2D,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          format);
+          size, GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   PrioritizedResource::Backing* backing = new PrioritizedResource::Backing(
       resource_id, resource_provider, size, format);
   memory_use_bytes_ += backing->bytes();
diff --git a/cc/resources/raster_source.h b/cc/resources/raster_source.h
index 929e49d..171d2a5 100644
--- a/cc/resources/raster_source.h
+++ b/cc/resources/raster_source.h
@@ -99,6 +99,8 @@
   // Return true if LCD anti-aliasing may be used when rastering text.
   virtual bool CanUseLCDText() const = 0;
 
+  virtual scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const = 0;
+
  protected:
   friend class base::RefCountedThreadSafe<RasterSource>;
 
diff --git a/cc/resources/recording_source.h b/cc/resources/recording_source.h
index 8915361..fdd8eb4 100644
--- a/cc/resources/recording_source.h
+++ b/cc/resources/recording_source.h
@@ -34,13 +34,13 @@
   // Return true iff the pile was modified.
   virtual bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                            Region* invalidation,
-                                           bool can_use_lcd_text,
                                            const gfx::Size& layer_size,
                                            const gfx::Rect& visible_layer_rect,
                                            int frame_number,
                                            RecordingMode recording_mode) = 0;
 
-  virtual scoped_refptr<RasterSource> CreateRasterSource() const = 0;
+  virtual scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const = 0;
 
   virtual gfx::Size GetSize() const = 0;
   virtual void SetEmptyBounds() = 0;
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 42ef4b3..115238f 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -266,7 +266,7 @@
       allow_overlay(false),
       read_lock_fence(NULL),
       size(),
-      origin(Internal),
+      origin(INTERNAL),
       target(0),
       original_filter(0),
       filter(0),
@@ -274,8 +274,8 @@
       bound_image_id(0),
       texture_pool(0),
       wrap_mode(0),
-      hint(TextureHintImmutable),
-      type(InvalidType),
+      hint(TEXTURE_HINT_IMMUTABLE),
+      type(RESOURCE_TYPE_INVALID),
       format(RGBA_8888),
       shared_bitmap(NULL),
       gpu_memory_buffer(NULL) {
@@ -322,12 +322,12 @@
       texture_pool(texture_pool),
       wrap_mode(wrap_mode),
       hint(hint),
-      type(GLTexture),
+      type(RESOURCE_TYPE_GL_TEXTURE),
       format(format),
       shared_bitmap(NULL),
       gpu_memory_buffer(NULL) {
   DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
-  DCHECK_EQ(origin == Internal, !!texture_pool);
+  DCHECK_EQ(origin == INTERNAL, !!texture_pool);
 }
 
 ResourceProvider::Resource::Resource(uint8_t* pixels,
@@ -365,13 +365,13 @@
       bound_image_id(0),
       texture_pool(0),
       wrap_mode(wrap_mode),
-      hint(TextureHintImmutable),
-      type(Bitmap),
+      hint(TEXTURE_HINT_IMMUTABLE),
+      type(RESOURCE_TYPE_BITMAP),
       format(RGBA_8888),
       shared_bitmap(bitmap),
       gpu_memory_buffer(NULL) {
   DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
-  DCHECK(origin == Delegated || pixels);
+  DCHECK(origin == DELEGATED || pixels);
   if (bitmap)
     shared_bitmap_id = bitmap->id();
 }
@@ -410,8 +410,8 @@
       bound_image_id(0),
       texture_pool(0),
       wrap_mode(wrap_mode),
-      hint(TextureHintImmutable),
-      type(Bitmap),
+      hint(TEXTURE_HINT_IMMUTABLE),
+      type(RESOURCE_TYPE_BITMAP),
       format(RGBA_8888),
       shared_bitmap_id(bitmap_id),
       shared_bitmap(NULL),
@@ -445,15 +445,15 @@
   else
     resource_provider->InitializeSoftware();
 
-  DCHECK_NE(InvalidType, resource_provider->default_resource_type());
+  DCHECK_NE(RESOURCE_TYPE_INVALID, resource_provider->default_resource_type());
   return resource_provider.Pass();
 }
 
 ResourceProvider::~ResourceProvider() {
   while (!children_.empty())
-    DestroyChildInternal(children_.begin(), ForShutdown);
+    DestroyChildInternal(children_.begin(), FOR_SHUTDOWN);
   while (!resources_.empty())
-    DeleteResourceInternal(resources_.begin(), ForShutdown);
+    DeleteResourceInternal(resources_.begin(), FOR_SHUTDOWN);
 
   CleanUpGLIfNeeded();
 }
@@ -481,17 +481,17 @@
     ResourceFormat format) {
   DCHECK(!size.IsEmpty());
   switch (default_resource_type_) {
-    case GLTexture:
+    case RESOURCE_TYPE_GL_TEXTURE:
       return CreateGLTexture(size,
                              GL_TEXTURE_2D,
                              GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
                              wrap_mode,
                              hint,
                              format);
-    case Bitmap:
+    case RESOURCE_TYPE_BITMAP:
       DCHECK_EQ(RGBA_8888, format);
       return CreateBitmap(size, wrap_mode);
-    case InvalidType:
+    case RESOURCE_TYPE_INVALID:
       break;
   }
 
@@ -507,17 +507,17 @@
     ResourceFormat format) {
   DCHECK(!size.IsEmpty());
   switch (default_resource_type_) {
-    case GLTexture:
+    case RESOURCE_TYPE_GL_TEXTURE:
       return CreateGLTexture(size,
                              target,
                              GL_TEXTURE_POOL_MANAGED_CHROMIUM,
                              wrap_mode,
                              hint,
                              format);
-    case Bitmap:
+    case RESOURCE_TYPE_BITMAP:
       DCHECK_EQ(RGBA_8888, format);
       return CreateBitmap(size, wrap_mode);
-    case InvalidType:
+    case RESOURCE_TYPE_INVALID:
       break;
   }
 
@@ -537,15 +537,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   ResourceId id = next_id_++;
-  Resource resource(0,
-                    size,
-                    Resource::Internal,
-                    target,
-                    GL_LINEAR,
-                    texture_pool,
-                    wrap_mode,
-                    hint,
-                    format);
+  Resource resource(0, size, Resource::INTERNAL, target, GL_LINEAR,
+                    texture_pool, wrap_mode, hint, format);
   resource.allocated = false;
   resources_[id] = resource;
   return id;
@@ -561,8 +554,8 @@
   DCHECK(pixels);
 
   ResourceId id = next_id_++;
-  Resource resource(
-      pixels, bitmap.release(), size, Resource::Internal, GL_LINEAR, wrap_mode);
+  Resource resource(pixels, bitmap.release(), size, Resource::INTERNAL,
+                    GL_LINEAR, wrap_mode);
   resource.allocated = true;
   resources_[id] = resource;
   return id;
@@ -574,15 +567,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   ResourceId id = next_id_++;
-  Resource resource(0,
-                    gfx::Size(),
-                    Resource::Internal,
-                    GL_TEXTURE_RECTANGLE_ARB,
-                    GL_LINEAR,
-                    GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
-                    GL_CLAMP_TO_EDGE,
-                    TextureHintImmutable,
-                    RGBA_8888);
+  Resource resource(0, gfx::Size(), Resource::INTERNAL,
+                    GL_TEXTURE_RECTANGLE_ARB, GL_LINEAR,
+                    GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, GL_CLAMP_TO_EDGE,
+                    TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   LazyCreate(&resource);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
@@ -603,22 +591,16 @@
   DCHECK(mailbox.IsValid());
   Resource& resource = resources_[id];
   if (mailbox.IsTexture()) {
-    resource = Resource(0,
-                        gfx::Size(),
-                        Resource::External,
-                        mailbox.target(),
-                        mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR,
-                        0,
-                        GL_CLAMP_TO_EDGE,
-                        TextureHintImmutable,
-                        RGBA_8888);
+    resource = Resource(0, gfx::Size(), Resource::EXTERNAL, mailbox.target(),
+                        mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR, 0,
+                        GL_CLAMP_TO_EDGE, TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   } else {
     DCHECK(mailbox.IsSharedMemory());
     SharedBitmap* shared_bitmap = mailbox.shared_bitmap();
     uint8_t* pixels = shared_bitmap->pixels();
     DCHECK(pixels);
     resource = Resource(pixels, shared_bitmap, mailbox.shared_memory_size(),
-                        Resource::External, GL_LINEAR, GL_CLAMP_TO_EDGE);
+                        Resource::EXTERNAL, GL_LINEAR, GL_CLAMP_TO_EDGE);
   }
   resource.allocated = true;
   resource.mailbox = mailbox;
@@ -642,7 +624,7 @@
     resource->marked_for_deletion = true;
     return;
   } else {
-    DeleteResourceInternal(it, Normal);
+    DeleteResourceInternal(it, NORMAL);
   }
 }
 
@@ -652,38 +634,38 @@
   Resource* resource = &it->second;
   bool lost_resource = resource->lost;
 
-  DCHECK(resource->exported_count == 0 || style != Normal);
-  if (style == ForShutdown && resource->exported_count > 0)
+  DCHECK(resource->exported_count == 0 || style != NORMAL);
+  if (style == FOR_SHUTDOWN && resource->exported_count > 0)
     lost_resource = true;
 
   if (resource->image_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DestroyImageCHROMIUM(resource->image_id));
   }
   if (resource->gl_upload_query_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DeleteQueriesEXT(1, &resource->gl_upload_query_id));
   }
   if (resource->gl_read_lock_query_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DeleteQueriesEXT(1, &resource->gl_read_lock_query_id));
   }
   if (resource->gl_pixel_buffer_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DeleteBuffers(1, &resource->gl_pixel_buffer_id));
   }
-  if (resource->origin == Resource::External) {
+  if (resource->origin == Resource::EXTERNAL) {
     DCHECK(resource->mailbox.IsValid());
     GLuint sync_point = resource->mailbox.sync_point();
-    if (resource->type == GLTexture) {
+    if (resource->type == RESOURCE_TYPE_GL_TEXTURE) {
       DCHECK(resource->mailbox.IsTexture());
       lost_resource |= lost_output_surface_;
       GLES2Interface* gl = ContextGL();
@@ -709,18 +691,18 @@
     resource->gl_id = 0;
   }
   if (resource->shared_bitmap) {
-    DCHECK(resource->origin != Resource::External);
-    DCHECK_EQ(Bitmap, resource->type);
+    DCHECK(resource->origin != Resource::EXTERNAL);
+    DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
     delete resource->shared_bitmap;
     resource->pixels = NULL;
   }
   if (resource->pixels) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     delete[] resource->pixels;
     resource->pixels = NULL;
   }
   if (resource->gpu_memory_buffer) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     delete resource->gpu_memory_buffer;
     resource->gpu_memory_buffer = NULL;
   }
@@ -740,12 +722,12 @@
   Resource* resource = GetResource(id);
   DCHECK(!resource->locked_for_write);
   DCHECK(!resource->lock_for_read_count);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(ReadLockFenceHasPassed(resource));
   LazyAllocate(resource);
 
-  if (resource->type == GLTexture) {
+  if (resource->type == RESOURCE_TYPE_GL_TEXTURE) {
     DCHECK(resource->gl_id);
     DCHECK(!resource->pending_set_pixels);
     DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D));
@@ -760,7 +742,7 @@
                               resource->format,
                               resource->size);
   } else {
-    DCHECK_EQ(Bitmap, resource->type);
+    DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
     DCHECK(resource->allocated);
     DCHECK_EQ(RGBA_8888, resource->format);
     DCHECK(source_rect.x() >= image_rect.x());
@@ -780,6 +762,54 @@
   }
 }
 
+void ResourceProvider::CopyToResource(ResourceId id,
+                                      const uint8_t* image,
+                                      const gfx::Size& image_size) {
+  Resource* resource = GetResource(id);
+  DCHECK(!resource->locked_for_write);
+  DCHECK(!resource->lock_for_read_count);
+  DCHECK(resource->origin == Resource::INTERNAL);
+  DCHECK_EQ(resource->exported_count, 0);
+  DCHECK(ReadLockFenceHasPassed(resource));
+  LazyAllocate(resource);
+
+  DCHECK_EQ(image_size.width(), resource->size.width());
+  DCHECK_EQ(image_size.height(), resource->size.height());
+
+  if (resource->type == RESOURCE_TYPE_BITMAP) {
+    DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
+    DCHECK(resource->allocated);
+    DCHECK_EQ(RGBA_8888, resource->format);
+    SkImageInfo source_info =
+        SkImageInfo::MakeN32Premul(image_size.width(), image_size.height());
+    size_t image_stride = image_size.width() * 4;
+
+    ScopedWriteLockSoftware lock(this, id);
+    SkCanvas dest(lock.sk_bitmap());
+    dest.writePixels(source_info, image, image_stride, 0, 0);
+  } else {
+    DCHECK(resource->gl_id);
+    DCHECK(!resource->pending_set_pixels);
+    DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D));
+    GLES2Interface* gl = ContextGL();
+    DCHECK(gl);
+    DCHECK(texture_uploader_.get());
+    gl->BindTexture(GL_TEXTURE_2D, resource->gl_id);
+
+    if (resource->format == ETC1) {
+      size_t num_bytes = static_cast<size_t>(image_size.width()) *
+                         image_size.height() * BitsPerPixel(ETC1) / 8;
+      gl->CompressedTexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(ETC1),
+                               image_size.width(), image_size.height(), 0,
+                               num_bytes, image);
+    } else {
+      gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_size.width(),
+                        image_size.height(), GLDataFormat(resource->format),
+                        GLDataType(resource->format), image);
+    }
+  }
+}
+
 size_t ResourceProvider::NumBlockingUploads() {
   if (!texture_uploader_)
     return 0;
@@ -858,8 +888,8 @@
 
   LazyCreate(resource);
 
-  if (resource->type == GLTexture && !resource->gl_id) {
-    DCHECK(resource->origin != Resource::Internal);
+  if (resource->type == RESOURCE_TYPE_GL_TEXTURE && !resource->gl_id) {
+    DCHECK(resource->origin != Resource::INTERNAL);
     DCHECK(resource->mailbox.IsTexture());
 
     // Mailbox sync_points must be processed by a call to
@@ -908,12 +938,12 @@
   if (resource->marked_for_deletion && !resource->lock_for_read_count) {
     if (!resource->child_id) {
       // The resource belongs to this ResourceProvider, so it can be destroyed.
-      DeleteResourceInternal(it, Normal);
+      DeleteResourceInternal(it, NORMAL);
     } else {
       ChildMap::iterator child_it = children_.find(resource->child_id);
       ResourceIdArray unused;
       unused.push_back(id);
-      DeleteAndReturnUnusedResourcesToChild(child_it, Normal, unused);
+      DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
     }
   }
 }
@@ -929,14 +959,14 @@
 bool ResourceProvider::CanLockForWrite(ResourceId id) {
   Resource* resource = GetResource(id);
   return !resource->locked_for_write && !resource->lock_for_read_count &&
-         !resource->exported_count && resource->origin == Resource::Internal &&
+         !resource->exported_count && resource->origin == Resource::INTERNAL &&
          !resource->lost && ReadLockFenceHasPassed(resource);
 }
 
 void ResourceProvider::UnlockForWrite(ResourceProvider::Resource* resource) {
   DCHECK(resource->locked_for_write);
   DCHECK_EQ(resource->exported_count, 0);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   resource->locked_for_write = false;
 }
 
@@ -1034,7 +1064,7 @@
       gpu_memory_buffer_(nullptr),
       size_(resource_->size),
       format_(resource_->format) {
-  DCHECK_EQ(GLTexture, resource_->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource_->type);
   std::swap(gpu_memory_buffer_, resource_->gpu_memory_buffer);
 }
 
@@ -1187,7 +1217,7 @@
       highp_threshold_min_(highp_threshold_min),
       next_id_(1),
       next_child_(1),
-      default_resource_type_(InvalidType),
+      default_resource_type_(RESOURCE_TYPE_INVALID),
       use_texture_storage_ext_(false),
       use_texture_format_bgra_(false),
       use_texture_usage_hint_(false),
@@ -1204,11 +1234,11 @@
 
 void ResourceProvider::InitializeSoftware() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_NE(Bitmap, default_resource_type_);
+  DCHECK_NE(RESOURCE_TYPE_BITMAP, default_resource_type_);
 
   CleanUpGLIfNeeded();
 
-  default_resource_type_ = Bitmap;
+  default_resource_type_ = RESOURCE_TYPE_BITMAP;
   // Pick an arbitrary limit here similar to what hardware might.
   max_texture_size_ = 16 * 1024;
   best_texture_format_ = RGBA_8888;
@@ -1217,11 +1247,11 @@
 void ResourceProvider::InitializeGL() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!texture_uploader_);
-  DCHECK_NE(GLTexture, default_resource_type_);
+  DCHECK_NE(RESOURCE_TYPE_GL_TEXTURE, default_resource_type_);
   DCHECK(!texture_id_allocator_);
   DCHECK(!buffer_id_allocator_);
 
-  default_resource_type_ = GLTexture;
+  default_resource_type_ = RESOURCE_TYPE_GL_TEXTURE;
 
   const ContextProvider::Capabilities& caps =
       output_surface_->context_provider()->ContextCapabilities();
@@ -1250,7 +1280,7 @@
 
 void ResourceProvider::CleanUpGLIfNeeded() {
   GLES2Interface* gl = ContextGL();
-  if (default_resource_type_ != GLTexture) {
+  if (default_resource_type_ != RESOURCE_TYPE_GL_TEXTURE) {
     // We are not in GL mode, but double check before returning.
     DCHECK(!gl);
     DCHECK(!texture_uploader_);
@@ -1263,7 +1293,7 @@
   for (ResourceMap::const_iterator itr = resources_.begin();
        itr != resources_.end();
        ++itr) {
-    DCHECK_NE(GLTexture, itr->second.type);
+    DCHECK_NE(RESOURCE_TYPE_GL_TEXTURE, itr->second.type);
   }
 #endif  // DCHECK_IS_ON()
 
@@ -1287,7 +1317,7 @@
 void ResourceProvider::DestroyChild(int child_id) {
   ChildMap::iterator it = children_.find(child_id);
   DCHECK(it != children_.end());
-  DestroyChildInternal(it, Normal);
+  DestroyChildInternal(it, NORMAL);
 }
 
 void ResourceProvider::DestroyChildInternal(ChildMap::iterator it,
@@ -1295,7 +1325,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   Child& child = it->second;
-  DCHECK(style == ForShutdown || !child.marked_for_deletion);
+  DCHECK(style == FOR_SHUTDOWN || !child.marked_for_deletion);
 
   ResourceIdArray resources_for_child;
 
@@ -1379,21 +1409,14 @@
     ResourceId local_id = next_id_++;
     Resource& resource = resources_[local_id];
     if (it->is_software) {
-      resource = Resource(it->mailbox_holder.mailbox,
-                          it->size,
-                          Resource::Delegated,
-                          GL_LINEAR,
-                          it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE);
+      resource =
+          Resource(it->mailbox_holder.mailbox, it->size, Resource::DELEGATED,
+                   GL_LINEAR, it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE);
     } else {
-      resource = Resource(0,
-                          it->size,
-                          Resource::Delegated,
-                          it->mailbox_holder.texture_target,
-                          it->filter,
-                          0,
+      resource = Resource(0, it->size, Resource::DELEGATED,
+                          it->mailbox_holder.texture_target, it->filter, 0,
                           it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE,
-                          TextureHintImmutable,
-                          it->format);
+                          TEXTURE_HINT_IMMUTABLE, it->format);
       resource.mailbox = TextureMailbox(it->mailbox_holder.mailbox,
                                         it->mailbox_holder.texture_target,
                                         it->mailbox_holder.sync_point);
@@ -1438,7 +1461,7 @@
     if (!resource_is_in_use)
       unused.push_back(local_id);
   }
-  DeleteAndReturnUnusedResourcesToChild(child_it, Normal, unused);
+  DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
 }
 
 // static
@@ -1505,7 +1528,7 @@
 
     if (returned.sync_point) {
       DCHECK(!resource->has_shared_bitmap_id);
-      if (resource->origin == Resource::Internal) {
+      if (resource->origin == Resource::INTERNAL) {
         DCHECK(resource->gl_id);
         GLC(gl, gl->WaitSyncPointCHROMIUM(returned.sync_point));
       } else {
@@ -1519,18 +1542,18 @@
 
     if (!resource->child_id) {
       // The resource belongs to this ResourceProvider, so it can be destroyed.
-      DeleteResourceInternal(map_iterator, Normal);
+      DeleteResourceInternal(map_iterator, NORMAL);
       continue;
     }
 
-    DCHECK(resource->origin == Resource::Delegated);
+    DCHECK(resource->origin == Resource::DELEGATED);
     // Delete the resource and return it to the child it came from one.
     if (resource->child_id != child_id) {
       if (child_id) {
         DCHECK_NE(resources_for_child.size(), 0u);
         DCHECK(child_it != children_.end());
-        DeleteAndReturnUnusedResourcesToChild(
-            child_it, Normal, resources_for_child);
+        DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL,
+                                              resources_for_child);
         resources_for_child.clear();
       }
 
@@ -1544,8 +1567,8 @@
   if (child_id) {
     DCHECK_NE(resources_for_child.size(), 0u);
     DCHECK(child_it != children_.end());
-    DeleteAndReturnUnusedResourcesToChild(
-        child_it, Normal, resources_for_child);
+    DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL,
+                                          resources_for_child);
   }
 }
 
@@ -1555,7 +1578,7 @@
   Resource* source = GetResource(id);
   DCHECK(!source->locked_for_write);
   DCHECK(!source->lock_for_read_count);
-  DCHECK(source->origin != Resource::External || source->mailbox.IsValid());
+  DCHECK(source->origin != Resource::EXTERNAL || source->mailbox.IsValid());
   DCHECK(source->allocated);
   resource->id = id;
   resource->format = source->format;
@@ -1565,13 +1588,13 @@
   resource->is_repeated = (source->wrap_mode == GL_REPEAT);
   resource->allow_overlay = source->allow_overlay;
 
-  if (source->type == Bitmap) {
+  if (source->type == RESOURCE_TYPE_BITMAP) {
     resource->mailbox_holder.mailbox = source->shared_bitmap_id;
     resource->is_software = true;
   } else if (!source->mailbox.IsValid()) {
     LazyCreate(source);
     DCHECK(source->gl_id);
-    DCHECK(source->origin == Resource::Internal);
+    DCHECK(source->origin == Resource::INTERNAL);
     GLC(gl,
         gl->BindTexture(resource->mailbox_holder.texture_target,
                         source->gl_id));
@@ -1590,7 +1613,7 @@
     DCHECK(source->mailbox.IsTexture());
     if (source->image_id && source->dirty_image) {
       DCHECK(source->gl_id);
-      DCHECK(source->origin == Resource::Internal);
+      DCHECK(source->origin == Resource::INTERNAL);
       GLC(gl,
           gl->BindTexture(resource->mailbox_holder.texture_target,
                           source->gl_id));
@@ -1635,9 +1658,10 @@
     DCHECK(child_info->child_to_parent_map.count(child_id));
 
     bool is_lost =
-        resource.lost || (resource.type == GLTexture && lost_output_surface_);
+        resource.lost ||
+        (resource.type == RESOURCE_TYPE_GL_TEXTURE && lost_output_surface_);
     if (resource.exported_count > 0 || resource.lock_for_read_count > 0) {
-      if (style != ForShutdown) {
+      if (style != FOR_SHUTDOWN) {
         // Defer this until we receive the resource back from the parent or
         // the read lock is released.
         resource.marked_for_deletion = true;
@@ -1666,7 +1690,7 @@
     ReturnedResource returned;
     returned.id = child_id;
     returned.sync_point = resource.mailbox.sync_point();
-    if (!returned.sync_point && resource.type == GLTexture)
+    if (!returned.sync_point && resource.type == RESOURCE_TYPE_GL_TEXTURE)
       need_sync_point = true;
     returned.count = resource.imported_count;
     returned.lost = is_lost;
@@ -1702,12 +1726,12 @@
                "ResourceProvider::AcquirePixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
   DCHECK_NE(ETC1, resource->format);
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
   if (!resource->gl_pixel_buffer_id)
@@ -1728,7 +1752,7 @@
                "ResourceProvider::ReleasePixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
 
@@ -1744,7 +1768,7 @@
     resource->locked_for_write = false;
   }
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   if (!resource->gl_pixel_buffer_id)
     return;
   GLES2Interface* gl = ContextGL();
@@ -1761,12 +1785,12 @@
                "ResourceProvider::MapPixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
 
   *stride = 0;
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
   DCHECK(resource->gl_pixel_buffer_id);
@@ -1785,11 +1809,11 @@
                "ResourceProvider::UnmapPixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
   DCHECK(resource->gl_pixel_buffer_id);
@@ -1833,7 +1857,7 @@
   DCHECK(!resource->pending_set_pixels);
 
   LazyCreate(resource);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK(resource->gl_id || resource->allocated);
   DCHECK(ReadLockFenceHasPassed(resource));
   DCHECK(!resource->image_id);
@@ -1842,7 +1866,7 @@
   resource->allocated = true;
   LockForWrite(id);
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   DCHECK(resource->gl_id);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
@@ -1944,14 +1968,15 @@
 }
 
 void ResourceProvider::LazyCreate(Resource* resource) {
-  if (resource->type != GLTexture || resource->origin != Resource::Internal)
+  if (resource->type != RESOURCE_TYPE_GL_TEXTURE ||
+      resource->origin != Resource::INTERNAL)
     return;
 
   if (resource->gl_id)
     return;
 
   DCHECK(resource->texture_pool);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK(!resource->mailbox.IsValid());
   resource->gl_id = texture_id_allocator_->NextId();
 
@@ -1975,7 +2000,7 @@
   GLC(gl,
       gl->TexParameteri(
           resource->target, GL_TEXTURE_POOL_CHROMIUM, resource->texture_pool));
-  if (use_texture_usage_hint_ && (resource->hint & TextureHintFramebuffer)) {
+  if (use_texture_usage_hint_ && (resource->hint & TEXTURE_HINT_FRAMEBUFFER)) {
     GLC(gl,
         gl->TexParameteri(resource->target,
                           GL_TEXTURE_USAGE_ANGLE,
@@ -2002,7 +2027,7 @@
   GLC(gl, gl->BindTexture(GL_TEXTURE_2D, resource->gl_id));
   if (use_texture_storage_ext_ &&
       IsFormatSupportedForStorage(format, use_texture_format_bgra_) &&
-      (resource->hint & TextureHintImmutable)) {
+      (resource->hint & TEXTURE_HINT_IMMUTABLE)) {
     GLenum storage_format = TextureToStorageFormat(format);
     GLC(gl,
         gl->TexStorage2DEXT(
@@ -2042,18 +2067,18 @@
 
   Resource* source_resource = GetResource(source_id);
   DCHECK(!source_resource->lock_for_read_count);
-  DCHECK(source_resource->origin == Resource::Internal);
+  DCHECK(source_resource->origin == Resource::INTERNAL);
   DCHECK_EQ(source_resource->exported_count, 0);
-  DCHECK_EQ(GLTexture, source_resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, source_resource->type);
   DCHECK(source_resource->allocated);
   LazyCreate(source_resource);
 
   Resource* dest_resource = GetResource(dest_id);
   DCHECK(!dest_resource->locked_for_write);
   DCHECK(!dest_resource->lock_for_read_count);
-  DCHECK(dest_resource->origin == Resource::Internal);
+  DCHECK(dest_resource->origin == Resource::INTERNAL);
   DCHECK_EQ(dest_resource->exported_count, 0);
-  DCHECK_EQ(GLTexture, dest_resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, dest_resource->type);
   LazyAllocate(dest_resource);
 
   DCHECK_EQ(source_resource->type, dest_resource->type);
@@ -2114,7 +2139,7 @@
   Resource* resource = GetResource(id);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(resource->allocated);
-  if (resource->type != GLTexture || resource->gl_id)
+  if (resource->type != RESOURCE_TYPE_GL_TEXTURE || resource->gl_id)
     return;
   if (!resource->mailbox.sync_point())
     return;
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index 167ef20..95e1294 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -67,16 +67,16 @@
   typedef std::set<ResourceId> ResourceIdSet;
   typedef base::hash_map<ResourceId, ResourceId> ResourceIdMap;
   enum TextureHint {
-    TextureHintDefault = 0x0,
-    TextureHintImmutable = 0x1,
-    TextureHintFramebuffer = 0x2,
-    TextureHintImmutableFramebuffer =
-        TextureHintImmutable | TextureHintFramebuffer
+    TEXTURE_HINT_DEFAULT = 0x0,
+    TEXTURE_HINT_IMMUTABLE = 0x1,
+    TEXTURE_HINT_FRAMEBUFFER = 0x2,
+    TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER =
+        TEXTURE_HINT_IMMUTABLE | TEXTURE_HINT_FRAMEBUFFER
   };
   enum ResourceType {
-    InvalidType = 0,
-    GLTexture = 1,
-    Bitmap,
+    RESOURCE_TYPE_INVALID = 0,
+    RESOURCE_TYPE_GL_TEXTURE = 1,
+    RESOURCE_TYPE_BITMAP,
   };
 
   static scoped_ptr<ResourceProvider> Create(
@@ -150,11 +150,15 @@
 
   // Update pixels from image, copying source_rect (in image) to dest_offset (in
   // the resource).
+  // NOTE: DEPRECATED. Use CopyToResource() instead.
   void SetPixels(ResourceId id,
                  const uint8_t* image,
                  const gfx::Rect& image_rect,
                  const gfx::Rect& source_rect,
                  const gfx::Vector2d& dest_offset);
+  void CopyToResource(ResourceId id,
+                      const uint8_t* image,
+                      const gfx::Size& image_size);
 
   // Check upload status.
   size_t NumBlockingUploads();
@@ -434,7 +438,7 @@
 
  private:
   struct Resource {
-    enum Origin { Internal, External, Delegated };
+    enum Origin { INTERNAL, EXTERNAL, DELEGATED };
 
     Resource();
     ~Resource();
@@ -547,8 +551,8 @@
                         ResourceId id,
                         TransferableResource* resource);
   enum DeleteStyle {
-    Normal,
-    ForShutdown,
+    NORMAL,
+    FOR_SHUTDOWN,
   };
   void DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style);
   void DeleteAndReturnUnusedResourcesToChild(ChildMap::iterator child_it,
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index 2bba801..fdd7fc0 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -354,14 +354,14 @@
                        uint8_t* pixels) {
   resource_provider->WaitSyncPointIfNeeded(id);
   switch (resource_provider->default_resource_type()) {
-    case ResourceProvider::GLTexture: {
+    case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE: {
       ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
       ASSERT_NE(0U, lock_gl.texture_id());
       context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
       context->GetPixels(size, format, pixels);
       break;
     }
-    case ResourceProvider::Bitmap: {
+    case ResourceProvider::RESOURCE_TYPE_BITMAP: {
       ResourceProvider::ScopedReadLockSoftware lock_software(resource_provider,
                                                              id);
       memcpy(pixels,
@@ -369,7 +369,7 @@
              lock_software.sk_bitmap()->getSize());
       break;
     }
-    case ResourceProvider::InvalidType:
+    case ResourceProvider::RESOURCE_TYPE_INVALID:
       NOTREACHED();
       break;
   }
@@ -384,7 +384,7 @@
         child_context_(NULL),
         main_thread_task_runner_(BlockingTaskRunner::Create(NULL)) {
     switch (GetParam()) {
-      case ResourceProvider::GLTexture: {
+      case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE: {
         scoped_ptr<ResourceProviderContext> context3d(
             ResourceProviderContext::Create(shared_data_.get()));
         context3d_ = context3d.get();
@@ -401,13 +401,13 @@
             FakeOutputSurface::Create3d(child_context_owned.Pass());
         break;
       }
-      case ResourceProvider::Bitmap:
+      case ResourceProvider::RESOURCE_TYPE_BITMAP:
         output_surface_ = FakeOutputSurface::CreateSoftware(
             make_scoped_ptr(new SoftwareOutputDevice));
         child_output_surface_ = FakeOutputSurface::CreateSoftware(
             make_scoped_ptr(new SoftwareOutputDevice));
         break;
-      case ResourceProvider::InvalidType:
+      case ResourceProvider::RESOURCE_TYPE_INVALID:
         NOTREACHED();
         break;
     }
@@ -458,7 +458,7 @@
                                                   bool* lost_resource,
                                                   bool* release_called,
                                                   uint32* sync_point) {
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       unsigned texture = child_context_->createTexture();
       gpu::Mailbox gpu_mailbox;
       child_context_->bindTexture(GL_TEXTURE_2D, texture);
@@ -516,15 +516,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   EXPECT_EQ(1, static_cast<int>(resource_provider->num_resources()));
-  if (expected_default_type == ResourceProvider::GLTexture)
+  if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     EXPECT_EQ(0u, context->NumTextures());
 
   uint8_t data[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
-  if (expected_default_type == ResourceProvider::GLTexture)
+  resource_provider->CopyToResource(id, data, size);
+  if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     EXPECT_EQ(1u, context->NumTextures());
 
   uint8_t result[4] = { 0 };
@@ -533,7 +532,7 @@
 
   resource_provider->DeleteResource(id);
   EXPECT_EQ(0, static_cast<int>(resource_provider->num_resources()));
-  if (expected_default_type == ResourceProvider::GLTexture)
+  if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     EXPECT_EQ(0u, context->NumTextures());
 }
 
@@ -548,7 +547,7 @@
   ASSERT_EQ(16U, pixel_size);
 
   ResourceProvider::ResourceId id = resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   uint8_t image[16] = { 0 };
   gfx::Rect image_rect(size);
@@ -608,8 +607,40 @@
   resource_provider_->DeleteResource(id);
 }
 
+TEST_P(ResourceProviderTest, SimpleUpload) {
+  gfx::Size size(2, 2);
+  ResourceFormat format = RGBA_8888;
+  size_t pixel_size = TextureSizeBytes(size, format);
+  ASSERT_EQ(16U, pixel_size);
+
+  ResourceProvider::ResourceId id = resource_provider_->CreateResource(
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
+
+  uint8_t image[16] = {0};
+  resource_provider_->CopyToResource(id, image, size);
+  {
+    uint8_t result[16] = {0};
+    uint8_t expected[16] = {0};
+    GetResourcePixels(resource_provider_.get(), context(), id, size, format,
+                      result);
+    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
+  }
+
+  for (uint8_t i = 0; i < pixel_size; ++i)
+    image[i] = i;
+  resource_provider_->CopyToResource(id, image, size);
+  {
+    uint8_t result[16] = {0};
+    uint8_t expected[16] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+    GetResourcePixels(resource_provider_.get(), context(), id, size, format,
+                      result);
+    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
+  }
+}
+
 TEST_P(ResourceProviderTest, TransferGLResources) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   gfx::Size size(1, 1);
   ResourceFormat format = RGBA_8888;
@@ -617,18 +648,17 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = { 5, 5, 5, 5 };
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   ResourceProvider::ResourceId id3 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   {
     ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
         child_resource_provider_.get(), id3);
@@ -855,16 +885,15 @@
 }
 
 TEST_P(ResourceProviderTest, ReadLockCountStopsReturnToChildOrDelete) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   gfx::Size size(1, 1);
   ResourceFormat format = RGBA_8888;
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = {1, 2, 3, 4};
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -908,7 +937,7 @@
 
 TEST_P(ResourceProviderTest, AllowOverlayTransfersToParent) {
   // Overlays only supported on the GL path.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   uint32 sync_point = 0;
@@ -958,7 +987,7 @@
 }
 
 TEST_P(ResourceProviderTest, TransferSoftwareResources) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   gfx::Size size(1, 1);
@@ -967,15 +996,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = { 5, 5, 5, 5 };
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   scoped_ptr<SharedBitmap> shared_bitmap(CreateAndFillSharedBitmap(
       shared_bitmap_manager_.get(), gfx::Size(1, 1), 0));
@@ -1143,7 +1171,7 @@
 }
 
 TEST_P(ResourceProviderTest, TransferGLToSoftware) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   scoped_ptr<ResourceProviderContext> child_context_owned(
@@ -1169,10 +1197,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider->CopyToResource(id1, data1, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1207,7 +1234,7 @@
 }
 
 TEST_P(ResourceProviderTest, TransferInvalidSoftware) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   gfx::Size size(1, 1);
@@ -1216,10 +1243,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1264,15 +1290,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = {5, 5, 5, 5};
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1286,7 +1311,7 @@
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1316,7 +1341,7 @@
     resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
 
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1342,7 +1367,7 @@
 
     EXPECT_EQ(0u, resource_provider_->num_resources());
     ASSERT_EQ(2u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, returned_to_child[0].sync_point);
       EXPECT_NE(0u, returned_to_child[1].sync_point);
     }
@@ -1358,15 +1383,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = {1, 2, 3, 4};
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = {5, 5, 5, 5};
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1380,7 +1404,7 @@
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1410,7 +1434,7 @@
     resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
 
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1445,7 +1469,7 @@
 
     EXPECT_EQ(1u, resource_provider_->num_resources());
     ASSERT_EQ(1u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, returned_to_child[0].sync_point);
     }
     EXPECT_FALSE(returned_to_child[0].lost);
@@ -1455,7 +1479,7 @@
     // lost at this point, and returned.
     resource_provider_ = nullptr;
     ASSERT_EQ(1u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, returned_to_child[0].sync_point);
     }
     EXPECT_TRUE(returned_to_child[0].lost);
@@ -1469,10 +1493,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id, data, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id, data, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1485,7 +1508,7 @@
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(1u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture)
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
     EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
     resource_provider_->ReceiveFromChild(child_id, list);
@@ -1505,7 +1528,7 @@
     resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
 
     ASSERT_EQ(1u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture)
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
       EXPECT_NE(0u, returned_to_child[0].sync_point);
     child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
   }
@@ -1519,10 +1542,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data[4] = {1, 2, 3, 4};
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id, data, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id, data, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1672,7 +1694,8 @@
     ASSERT_EQ(4U, pixel_size);
 
     ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
-        size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+        size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+        format);
 
     // The new texture is created with GL_LINEAR.
     EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id))
@@ -1695,10 +1718,9 @@
     Mock::VerifyAndClearExpectations(child_context);
 
     uint8_t data[4] = { 1, 2, 3, 4 };
-    gfx::Rect rect(size);
 
     EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id));
-    child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
+    child_resource_provider->CopyToResource(id, data, size);
     Mock::VerifyAndClearExpectations(child_context);
 
     // The texture is set to |child_filter| in the child.
@@ -1798,20 +1820,20 @@
 };
 
 TEST_P(ResourceProviderTest, TextureFilters_ChildNearestParentLinear) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   ResourceProviderTestTextureFilters::RunTest(GL_NEAREST, GL_LINEAR);
 }
 
 TEST_P(ResourceProviderTest, TextureFilters_ChildLinearParentNearest) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   ResourceProviderTestTextureFilters::RunTest(GL_LINEAR, GL_NEAREST);
 }
 
 TEST_P(ResourceProviderTest, TransferMailboxResources) {
   // Other mailbox transfers tested elsewhere.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   unsigned texture = context()->createTexture();
   context()->bindTexture(GL_TEXTURE_2D, texture);
@@ -1943,13 +1965,12 @@
   ResourceFormat format = RGBA_8888;
   ResourceProvider::ResourceId resource =
       child_resource_provider_->CreateResource(
-          size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           format);
   child_resource_provider_->AllocateForTesting(resource);
   // Expect a GL resource to be lost.
-  bool should_lose_resource = GetParam() == ResourceProvider::GLTexture;
+  bool should_lose_resource =
+      GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE;
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1999,9 +2020,7 @@
   ResourceFormat format = RGBA_8888;
   ResourceProvider::ResourceId resource =
       child_resource_provider_->CreateResource(
-          size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           format);
   child_resource_provider_->AllocateForTesting(resource);
 
@@ -2112,7 +2131,7 @@
     ASSERT_EQ(1u, returned_to_child.size());
     // Losing an output surface only loses hardware resources.
     EXPECT_EQ(returned_to_child[0].lost,
-              GetParam() == ResourceProvider::GLTexture);
+              GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE);
     child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
     returned_to_child.clear();
   }
@@ -2120,7 +2139,8 @@
   // Delete the resource in the child. Expect the resource to be lost if it's
   // a GL texture.
   child_resource_provider_->DeleteResource(resource);
-  EXPECT_EQ(lost_resource, GetParam() == ResourceProvider::GLTexture);
+  EXPECT_EQ(lost_resource,
+            GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE);
 }
 
 TEST_P(ResourceProviderTest, LostMailboxInGrandParent) {
@@ -2204,7 +2224,7 @@
 
   child_resource_provider_ = nullptr;
 
-  if (GetParam() == ResourceProvider::GLTexture) {
+  if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
     EXPECT_LE(sync_point, release_sync_point);
   }
   EXPECT_TRUE(release_called);
@@ -2238,7 +2258,7 @@
 
 TEST_P(ResourceProviderTest, LostContext) {
   // TextureMailbox callbacks only exist for GL textures for now.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   unsigned texture = context()->createTexture();
   context()->bindTexture(GL_TEXTURE_2D, texture);
@@ -2274,7 +2294,7 @@
 
 TEST_P(ResourceProviderTest, ScopedSampler) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2300,7 +2320,7 @@
   int texture_id = 1;
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   // Check that the texture gets created with the right sampler settings.
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id))
@@ -2361,7 +2381,7 @@
 
 TEST_P(ResourceProviderTest, ManagedResource) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2388,11 +2408,8 @@
 
   // Check that the texture gets created with the right sampler settings.
   ResourceProvider::ResourceId id = resource_provider->CreateManagedResource(
-      size,
-      GL_TEXTURE_2D,
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
-      format);
+      size, GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
   EXPECT_CALL(*context,
               texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -2416,7 +2433,7 @@
 
 TEST_P(ResourceProviderTest, TextureWrapMode) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2445,12 +2462,8 @@
     GLint wrap_mode = texture_id == 1 ? GL_CLAMP_TO_EDGE : GL_REPEAT;
     // Check that the texture gets created with the right sampler settings.
     ResourceProvider::ResourceId id = resource_provider->CreateGLTexture(
-        size,
-        GL_TEXTURE_2D,
-        texture_pool,
-        wrap_mode,
-        ResourceProvider::TextureHintImmutable,
-        format);
+        size, GL_TEXTURE_2D, texture_pool, wrap_mode,
+        ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
     EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
     EXPECT_CALL(*context,
                 texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -2473,7 +2486,7 @@
 
 TEST_P(ResourceProviderTest, TextureHint) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2501,10 +2514,10 @@
   GLenum texture_pool = GL_TEXTURE_POOL_UNMANAGED_CHROMIUM;
 
   const ResourceProvider::TextureHint hints[4] = {
-      ResourceProvider::TextureHintDefault,
-      ResourceProvider::TextureHintImmutable,
-      ResourceProvider::TextureHintFramebuffer,
-      ResourceProvider::TextureHintImmutableFramebuffer,
+      ResourceProvider::TEXTURE_HINT_DEFAULT,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
   };
   for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
     // Check that the texture gets created with the right sampler settings.
@@ -2530,9 +2543,9 @@
                 texParameteri(GL_TEXTURE_2D,
                               GL_TEXTURE_POOL_CHROMIUM,
                               GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
-    // Check only TextureHintFramebuffer set GL_TEXTURE_USAGE_ANGLE.
+    // Check only TEXTURE_HINT_FRAMEBUFFER set GL_TEXTURE_USAGE_ANGLE.
     bool is_framebuffer_hint =
-        hints[texture_id - 1] & ResourceProvider::TextureHintFramebuffer;
+        hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_FRAMEBUFFER;
     EXPECT_CALL(*context,
                 texParameteri(GL_TEXTURE_2D,
                               GL_TEXTURE_USAGE_ANGLE,
@@ -2546,7 +2559,7 @@
 }
 
 TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   gfx::Size size(64, 64);
@@ -2700,7 +2713,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_LinearToLinear) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2713,7 +2726,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_NearestToNearest) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2726,7 +2739,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_NearestToLinear) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2739,7 +2752,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_LinearToNearest) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2752,7 +2765,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2827,7 +2840,7 @@
 TEST_P(ResourceProviderTest,
        TextureMailbox_WaitSyncPointIfNeeded_WithSyncPoint) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2886,7 +2899,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_WaitSyncPointIfNeeded_NoSyncPoint) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -3013,7 +3026,7 @@
 
 TEST_P(ResourceProviderTest, TextureAllocation) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3035,7 +3048,6 @@
 
   gfx::Size size(2, 2);
   gfx::Vector2d offset(0, 0);
-  gfx::Rect rect(0, 0, 2, 2);
   ResourceFormat format = RGBA_8888;
   ResourceProvider::ResourceId id = 0;
   uint8_t pixels[16] = { 0 };
@@ -3043,7 +3055,7 @@
 
   // Lazy allocation. Don't allocate when creating the resource.
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
@@ -3056,13 +3068,13 @@
 
   // Do allocate when we set the pixels.
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
   EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _)).Times(1);
   EXPECT_CALL(*context, texSubImage2D(_, _, _, _, 2, 2, _, _, _)).Times(1);
-  resource_provider->SetPixels(id, pixels, rect, rect, offset);
+  resource_provider->CopyToResource(id, pixels, size);
 
   EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
   resource_provider->DeleteResource(id);
@@ -3071,7 +3083,7 @@
 
   // Same for async version.
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   resource_provider->AcquirePixelBuffer(id);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
@@ -3091,7 +3103,7 @@
 
 TEST_P(ResourceProviderTest, TextureAllocationHint) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3117,10 +3129,10 @@
 
   const ResourceFormat formats[2] = {RGBA_8888, BGRA_8888};
   const ResourceProvider::TextureHint hints[4] = {
-      ResourceProvider::TextureHintDefault,
-      ResourceProvider::TextureHintImmutable,
-      ResourceProvider::TextureHintFramebuffer,
-      ResourceProvider::TextureHintImmutableFramebuffer,
+      ResourceProvider::TEXTURE_HINT_DEFAULT,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
   };
   for (size_t i = 0; i < arraysize(formats); ++i) {
     for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
@@ -3131,7 +3143,7 @@
       EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
       EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
       bool is_immutable_hint =
-          hints[texture_id - 1] & ResourceProvider::TextureHintImmutable;
+          hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_IMMUTABLE;
       bool support_immutable_texture =
           is_immutable_hint && formats[i] == RGBA_8888;
       EXPECT_CALL(*context, texStorage2DEXT(_, _, _, 2, 2))
@@ -3150,7 +3162,7 @@
 
 TEST_P(ResourceProviderTest, TextureAllocationHint_BGRA) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3177,10 +3189,10 @@
   const ResourceFormat formats[2] = {RGBA_8888, BGRA_8888};
 
   const ResourceProvider::TextureHint hints[4] = {
-      ResourceProvider::TextureHintDefault,
-      ResourceProvider::TextureHintImmutable,
-      ResourceProvider::TextureHintFramebuffer,
-      ResourceProvider::TextureHintImmutableFramebuffer,
+      ResourceProvider::TEXTURE_HINT_DEFAULT,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
   };
   for (size_t i = 0; i < arraysize(formats); ++i) {
     for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
@@ -3191,7 +3203,7 @@
       EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
       EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
       bool is_immutable_hint =
-          hints[texture_id - 1] & ResourceProvider::TextureHintImmutable;
+          hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_IMMUTABLE;
       EXPECT_CALL(*context, texStorage2DEXT(_, _, _, 2, 2))
           .Times(is_immutable_hint ? 1 : 0);
       EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _))
@@ -3207,7 +3219,7 @@
 }
 
 TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3233,7 +3245,7 @@
                                1));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   resource_provider->AcquirePixelBuffer(id);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
@@ -3254,7 +3266,7 @@
 
 TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3280,7 +3292,7 @@
                                1));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   resource_provider->AcquirePixelBuffer(id);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
@@ -3329,7 +3341,7 @@
   EXPECT_CALL(*context, NextTextureId()).WillRepeatedly(Return(texture_id));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
                                GL_INNOCENT_CONTEXT_RESET_ARB);
 
@@ -3343,7 +3355,7 @@
 
 TEST_P(ResourceProviderTest, Image_GLTexture) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3372,7 +3384,7 @@
                                1));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, createImageCHROMIUM(_, kWidth, kHeight, GL_RGBA))
       .WillOnce(Return(kImageId))
@@ -3427,7 +3439,7 @@
 }
 
 TEST_P(ResourceProviderTest, CopyResource_GLTexture) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3459,7 +3471,7 @@
                                1));
 
   source_id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, createImageCHROMIUM(_, kWidth, kHeight, GL_RGBA))
       .WillOnce(Return(kImageId))
@@ -3472,7 +3484,7 @@
   Mock::VerifyAndClearExpectations(context);
 
   dest_id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, NextTextureId())
       .WillOnce(Return(kDestTextureId))
@@ -3521,7 +3533,8 @@
   output_surface->InitializeAndSetContext3d(context_provider, nullptr);
   resource_provider->InitializeGL();
 
-  CheckCreateResource(ResourceProvider::GLTexture, resource_provider, context);
+  CheckCreateResource(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE,
+                      resource_provider, context);
 }
 
 TEST(ResourceProviderTest, BasicInitializeGLSoftware) {
@@ -3544,7 +3557,8 @@
                                false,
                                1));
 
-  CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
+  CheckCreateResource(ResourceProvider::RESOURCE_TYPE_BITMAP,
+                      resource_provider.get(), NULL);
 
   InitializeGLAndCheck(shared_data.get(),
                        resource_provider.get(),
@@ -3552,7 +3566,8 @@
 
   resource_provider->InitializeSoftware();
   output_surface->ReleaseGL();
-  CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
+  CheckCreateResource(ResourceProvider::RESOURCE_TYPE_BITMAP,
+                      resource_provider.get(), NULL);
 
   InitializeGLAndCheck(shared_data.get(),
                        resource_provider.get(),
@@ -3560,7 +3575,7 @@
 }
 
 TEST_P(ResourceProviderTest, CompressedTextureETC1Allocate) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<AllocationTrackingContext3D> context_owned(
@@ -3585,7 +3600,7 @@
   int texture_id = 123;
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, ETC1);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, ETC1);
   EXPECT_NE(0u, id);
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
@@ -3595,8 +3610,8 @@
   resource_provider->DeleteResource(id);
 }
 
-TEST_P(ResourceProviderTest, CompressedTextureETC1SetPixels) {
-  if (GetParam() != ResourceProvider::GLTexture)
+TEST_P(ResourceProviderTest, CompressedTextureETC1Upload) {
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<AllocationTrackingContext3D> context_owned(
@@ -3622,15 +3637,14 @@
   uint8_t pixels[8];
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, ETC1);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, ETC1);
   EXPECT_NE(0u, id);
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
   EXPECT_CALL(*context,
               compressedTexImage2D(
                   _, 0, _, size.width(), size.height(), _, _, _)).Times(1);
-  resource_provider->SetPixels(
-      id, pixels, gfx::Rect(size), gfx::Rect(size), gfx::Vector2d(0, 0));
+  resource_provider->CopyToResource(id, pixels, size);
 
   EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
   resource_provider->DeleteResource(id);
@@ -3639,7 +3653,8 @@
 INSTANTIATE_TEST_CASE_P(
     ResourceProviderTests,
     ResourceProviderTest,
-    ::testing::Values(ResourceProvider::GLTexture, ResourceProvider::Bitmap));
+    ::testing::Values(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE,
+                      ResourceProvider::RESOURCE_TYPE_BITMAP));
 
 class TextureIdAllocationTrackingContext : public TestWebGraphicsContext3D {
  public:
@@ -3681,7 +3696,8 @@
                                  kTextureAllocationChunkSize));
 
     ResourceProvider::ResourceId id = resource_provider->CreateResource(
-        size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+        size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+        format);
     resource_provider->AllocateForTesting(id);
     Mock::VerifyAndClearExpectations(context);
 
@@ -3701,7 +3717,8 @@
                                  kTextureAllocationChunkSize));
 
     ResourceProvider::ResourceId id = resource_provider->CreateResource(
-        size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+        size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+        format);
     resource_provider->AllocateForTesting(id);
     Mock::VerifyAndClearExpectations(context);
 
diff --git a/cc/resources/scoped_resource.cc b/cc/resources/scoped_resource.cc
index 3b13559..e701182 100644
--- a/cc/resources/scoped_resource.cc
+++ b/cc/resources/scoped_resource.cc
@@ -38,10 +38,7 @@
 
   set_dimensions(size, format);
   set_id(resource_provider_->CreateManagedResource(
-      size,
-      target,
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      size, target, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       format));
 
 #if DCHECK_IS_ON()
diff --git a/cc/resources/scoped_resource_unittest.cc b/cc/resources/scoped_resource_unittest.cc
index a141218..b95d814 100644
--- a/cc/resources/scoped_resource_unittest.cc
+++ b/cc/resources/scoped_resource_unittest.cc
@@ -57,8 +57,8 @@
                                1));
   scoped_ptr<ScopedResource> texture =
       ScopedResource::Create(resource_provider.get());
-  texture->Allocate(
-      gfx::Size(30, 30), ResourceProvider::TextureHintImmutable, RGBA_8888);
+  texture->Allocate(gfx::Size(30, 30), ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                    RGBA_8888);
 
   // The texture has an allocated byte-size now.
   size_t expected_bytes = 30 * 30 * 4;
@@ -89,8 +89,8 @@
         ScopedResource::Create(resource_provider.get());
 
     EXPECT_EQ(0u, resource_provider->num_resources());
-    texture->Allocate(
-        gfx::Size(30, 30), ResourceProvider::TextureHintImmutable, RGBA_8888);
+    texture->Allocate(gfx::Size(30, 30),
+                      ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
     EXPECT_LT(0u, texture->id());
     EXPECT_EQ(1u, resource_provider->num_resources());
   }
@@ -100,8 +100,8 @@
     scoped_ptr<ScopedResource> texture =
         ScopedResource::Create(resource_provider.get());
     EXPECT_EQ(0u, resource_provider->num_resources());
-    texture->Allocate(
-        gfx::Size(30, 30), ResourceProvider::TextureHintImmutable, RGBA_8888);
+    texture->Allocate(gfx::Size(30, 30),
+                      ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
     EXPECT_LT(0u, texture->id());
     EXPECT_EQ(1u, resource_provider->num_resources());
     texture->Free();
diff --git a/cc/resources/texture_uploader.cc b/cc/resources/texture_uploader.cc
index 3d0f681..601fa8f 100644
--- a/cc/resources/texture_uploader.cc
+++ b/cc/resources/texture_uploader.cc
@@ -146,14 +146,7 @@
   if (is_full_upload)
     BeginQuery();
 
-  if (format == ETC1) {
-    // ETC1 does not support subimage uploads.
-    DCHECK(is_full_upload);
-    UploadWithTexImageETC1(image, size);
-  } else {
-    UploadWithMapTexSubImage(
-        image, image_rect, source_rect, dest_offset, format);
-  }
+  UploadWithMapTexSubImage(image, image_rect, source_rect, dest_offset, format);
 
   if (is_full_upload)
     EndQuery();
@@ -291,22 +284,6 @@
   gl_->UnmapTexSubImage2DCHROMIUM(pixel_dest);
 }
 
-void TextureUploader::UploadWithTexImageETC1(const uint8* image,
-                                             const gfx::Size& size) {
-  TRACE_EVENT0("cc", "TextureUploader::UploadWithTexImageETC1");
-  DCHECK_EQ(0, size.width() % 4);
-  DCHECK_EQ(0, size.height() % 4);
-
-  gl_->CompressedTexImage2D(GL_TEXTURE_2D,
-                            0,
-                            GLInternalFormat(ETC1),
-                            size.width(),
-                            size.height(),
-                            0,
-                            Resource::MemorySizeBytes(size, ETC1),
-                            image);
-}
-
 void TextureUploader::ProcessQueries() {
   while (!pending_queries_.empty()) {
     if (pending_queries_.front()->IsPending())
diff --git a/cc/resources/tile.cc b/cc/resources/tile.cc
index 39d48a8..1576047 100644
--- a/cc/resources/tile.cc
+++ b/cc/resources/tile.cc
@@ -39,7 +39,7 @@
       id_(s_next_id_++),
       scheduled_priority_(0) {
   set_raster_source(raster_source);
-  for (int i = 0; i < NUM_TREES; i++)
+  for (int i = 0; i <= LAST_TREE; i++)
     is_occluded_[i] = false;
 }
 
diff --git a/cc/resources/tile.h b/cc/resources/tile.h
index 698f43d..c3ca623 100644
--- a/cc/resources/tile.h
+++ b/cc/resources/tile.h
@@ -88,7 +88,7 @@
 
   bool HasResource() const { return draw_info_.has_resource(); }
   bool NeedsRaster() const {
-    return draw_info_.mode() == TileDrawInfo::PICTURE_PILE_MODE ||
+    return draw_info_.mode() == TileDrawInfo::OOM_MODE ||
            !draw_info_.IsReadyToDraw();
   }
 
@@ -150,9 +150,9 @@
   gfx::Size desired_texture_size_;
   gfx::Rect content_rect_;
   float contents_scale_;
-  bool is_occluded_[NUM_TREES];
+  bool is_occluded_[LAST_TREE + 1];
 
-  TilePriority priority_[NUM_TREES];
+  TilePriority priority_[LAST_TREE + 1];
   TileDrawInfo draw_info_;
 
   int layer_id_;
diff --git a/cc/resources/tile_draw_info.cc b/cc/resources/tile_draw_info.cc
index 7761d72..e75ea59 100644
--- a/cc/resources/tile_draw_info.cc
+++ b/cc/resources/tile_draw_info.cc
@@ -21,7 +21,7 @@
     case RESOURCE_MODE:
       return !!resource_;
     case SOLID_COLOR_MODE:
-    case PICTURE_PILE_MODE:
+    case OOM_MODE:
       return true;
   }
   NOTREACHED();
diff --git a/cc/resources/tile_draw_info.h b/cc/resources/tile_draw_info.h
index 54ca946..0c6bbf6 100644
--- a/cc/resources/tile_draw_info.h
+++ b/cc/resources/tile_draw_info.h
@@ -17,7 +17,7 @@
 // This class holds all the state relevant to drawing a tile.
 class CC_EXPORT TileDrawInfo {
  public:
-  enum Mode { RESOURCE_MODE, SOLID_COLOR_MODE, PICTURE_PILE_MODE };
+  enum Mode { RESOURCE_MODE, SOLID_COLOR_MODE, OOM_MODE };
 
   TileDrawInfo();
   ~TileDrawInfo();
@@ -49,7 +49,7 @@
   }
 
   bool requires_resource() const {
-    return mode_ == RESOURCE_MODE || mode_ == PICTURE_PILE_MODE;
+    return mode_ == RESOURCE_MODE || mode_ == OOM_MODE;
   }
 
   inline bool has_resource() const { return !!resource_; }
@@ -72,7 +72,7 @@
     solid_color_ = color;
   }
 
-  void set_rasterize_on_demand() { mode_ = PICTURE_PILE_MODE; }
+  void set_oom() { mode_ = OOM_MODE; }
 
   Mode mode_;
   SkColor solid_color_;
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc
index 94b6332..00ae3da 100644
--- a/cc/resources/tile_manager.cc
+++ b/cc/resources/tile_manager.cc
@@ -439,11 +439,11 @@
       global_state_.tree_priority,
       RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
 
-  // Use on-demand raster for any tiles that have not been been assigned
-  // memory. This ensures that we draw even when OOM.
+  // Change to OOM mode for any tiles that have not been been assigned memory.
+  // This ensures that we draw even when OOM.
   for (; !required_for_draw_queue->IsEmpty(); required_for_draw_queue->Pop()) {
     Tile* tile = required_for_draw_queue->Top();
-    tile->draw_info().set_rasterize_on_demand();
+    tile->draw_info().set_oom();
     client_->NotifyTileStateChanged(tile);
   }
 
@@ -604,8 +604,8 @@
     TileDrawInfo& draw_info = tile->draw_info();
     tile->scheduled_priority_ = schedule_priority++;
 
-    DCHECK(draw_info.mode() == TileDrawInfo::PICTURE_PILE_MODE ||
-           !draw_info.IsReadyToDraw());
+    DCHECK_IMPLIES(draw_info.mode() != TileDrawInfo::OOM_MODE,
+                   !draw_info.IsReadyToDraw());
 
     // If the tile already has a raster_task, then the memory used by it is
     // already accounted for in memory_usage. Otherwise, we'll have to acquire
@@ -973,31 +973,30 @@
   // Likewise if we don't allow any tiles (as is the case when we're
   // invisible), if we have tiles that aren't ready, then we shouldn't
   // activate as activation can cause checkerboards.
-  bool allow_rasterize_on_demand =
-      global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY &&
-      global_state_.memory_limit_policy != ALLOW_NOTHING;
+  bool wait_for_all_required_tiles =
+      global_state_.tree_priority == SMOOTHNESS_TAKES_PRIORITY ||
+      global_state_.memory_limit_policy == ALLOW_NOTHING;
 
-  // Use on-demand raster for any required-for-activation tiles that have
-  // not been been assigned memory after reaching a steady memory state. This
-  // ensures that we activate even when OOM. Note that we can't reuse the queue
-  // we used for AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call
-  // could have evicted some tiles that would not be picked up by the old raster
-  // queue.
+  // Mark any required-for-activation tiles that have not been been assigned
+  // memory after reaching a steady memory state as OOM. This ensures that we
+  // activate even when OOM. Note that we can't reuse the queue we used for
+  // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
+  // evicted some tiles that would not be picked up by the old raster queue.
   scoped_ptr<RasterTilePriorityQueue> required_for_activation_queue(
       client_->BuildRasterQueue(
           global_state_.tree_priority,
           RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION));
 
-  // If we have tiles to mark as rasterize on demand, but we don't allow
-  // rasterize on demand, then skip activation and return early.
-  if (!required_for_activation_queue->IsEmpty() && !allow_rasterize_on_demand)
+  // If we have tiles left to raster for activation, and we don't allow
+  // activating without them, then skip activation and return early.
+  if (!required_for_activation_queue->IsEmpty() && wait_for_all_required_tiles)
     return;
 
-  // Mark required tiles as rasterize on demand.
+  // Mark required tiles as OOM so that we can activate without them.
   for (; !required_for_activation_queue->IsEmpty();
        required_for_activation_queue->Pop()) {
     Tile* tile = required_for_activation_queue->Top();
-    tile->draw_info().set_rasterize_on_demand();
+    tile->draw_info().set_oom();
     client_->NotifyTileStateChanged(tile);
   }
 
diff --git a/cc/resources/tile_manager_perftest.cc b/cc/resources/tile_manager_perftest.cc
index 7c316ac..87d1837 100644
--- a/cc/resources/tile_manager_perftest.cc
+++ b/cc/resources/tile_manager_perftest.cc
@@ -355,7 +355,8 @@
       ++next_id;
     }
 
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
     for (FakePictureLayerImpl* layer : layers)
       layer->CreateAllTiles();
 
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc
index 4f441b3..19ccf24 100644
--- a/cc/resources/tile_manager_unittest.cc
+++ b/cc/resources/tile_manager_unittest.cc
@@ -127,7 +127,8 @@
         host_impl_.pending_tree()->LayerById(id_));
 
     // Add tilings/tiles for the layer.
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
   }
 
   TileManager* tile_manager() { return host_impl_.tile_manager(); }
@@ -541,7 +542,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   host_impl_.SetRequiresHighResToDraw();
   scoped_ptr<RasterTilePriorityQueue> queue(host_impl_.BuildRasterQueue(
@@ -771,7 +773,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   ActivateTree();
   SetupPendingTree(pending_pile);
@@ -909,14 +912,15 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   pending_child_layer->SetOpacity(0.0);
 
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // Renew all of the tile priorities.
   gfx::Rect viewport(layer_bounds);
diff --git a/cc/resources/tile_priority.h b/cc/resources/tile_priority.h
index 5067aa6..5247b3c 100644
--- a/cc/resources/tile_priority.h
+++ b/cc/resources/tile_priority.h
@@ -24,7 +24,7 @@
   // e.g. in Tile::priority_.
   ACTIVE_TREE = 0,
   PENDING_TREE = 1,
-  NUM_TREES = 2
+  LAST_TREE = 1
   // Be sure to update WhichTreeAsValue when adding new fields.
 };
 scoped_ptr<base::Value> WhichTreeAsValue(WhichTree tree);
@@ -118,7 +118,7 @@
   SAME_PRIORITY_FOR_BOTH_TREES,
   SMOOTHNESS_TAKES_PRIORITY,
   NEW_CONTENT_TAKES_PRIORITY,
-  NUM_TREE_PRIORITIES
+  LAST_TREE_PRIORITY = NEW_CONTENT_TAKES_PRIORITY
   // Be sure to update TreePriorityAsValue when adding new fields.
 };
 std::string TreePriorityToString(TreePriority prio);
diff --git a/cc/resources/tile_task_worker_pool_perftest.cc b/cc/resources/tile_task_worker_pool_perftest.cc
index 450bcbe..71da7b1 100644
--- a/cc/resources/tile_task_worker_pool_perftest.cc
+++ b/cc/resources/tile_task_worker_pool_perftest.cc
@@ -206,7 +206,7 @@
     for (unsigned i = 0; i < num_raster_tasks; ++i) {
       scoped_ptr<ScopedResource> resource(
           ScopedResource::Create(resource_provider_.get()));
-      resource->Allocate(size, ResourceProvider::TextureHintImmutable,
+      resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
                          RGBA_8888);
 
       ImageDecodeTask::Vector dependencies = image_decode_tasks;
@@ -270,6 +270,8 @@
         break;
       case TILE_TASK_WORKER_POOL_TYPE_GPU:
         Create3dOutputSurfaceAndResourceProvider();
+        rasterizer_ = GpuRasterizer::Create(
+            context_provider_.get(), resource_provider_.get(), false, false, 0);
         tile_task_worker_pool_ = GpuTileTaskWorkerPool::Create(
             task_runner_.get(), task_graph_runner_.get(),
             static_cast<GpuRasterizer*>(rasterizer_.get()));
diff --git a/cc/resources/tile_task_worker_pool_unittest.cc b/cc/resources/tile_task_worker_pool_unittest.cc
index 1e84674..d7cb155 100644
--- a/cc/resources/tile_task_worker_pool_unittest.cc
+++ b/cc/resources/tile_task_worker_pool_unittest.cc
@@ -240,7 +240,8 @@
   void AppendTask(unsigned id, const gfx::Size& size) {
     scoped_ptr<ScopedResource> resource(
         ScopedResource::Create(resource_provider_.get()));
-    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
+    resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                       RGBA_8888);
     const Resource* const_resource = resource.get();
 
     ImageDecodeTask::Vector empty;
@@ -258,7 +259,8 @@
 
     scoped_ptr<ScopedResource> resource(
         ScopedResource::Create(resource_provider_.get()));
-    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
+    resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                       RGBA_8888);
     const Resource* const_resource = resource.get();
 
     ImageDecodeTask::Vector empty;
@@ -387,7 +389,8 @@
     // Verify a resource of this size is larger than the transfer buffer.
     scoped_ptr<ScopedResource> resource(
         ScopedResource::Create(resource_provider_.get()));
-    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
+    resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                       RGBA_8888);
     EXPECT_GE(resource->bytes(), kMaxTransferBufferUsageBytes);
   }
 
diff --git a/cc/resources/tiling_set_eviction_queue.cc b/cc/resources/tiling_set_eviction_queue.cc
index f13c915..1f0e213 100644
--- a/cc/resources/tiling_set_eviction_queue.cc
+++ b/cc/resources/tiling_set_eviction_queue.cc
@@ -330,9 +330,6 @@
       return false;
     case SAME_PRIORITY_FOR_BOTH_TREES:
       break;
-    case NUM_TREE_PRIORITIES:
-      NOTREACHED();
-      break;
   }
 
   // The priority for tile priority of a shared tile will be a combined
diff --git a/cc/resources/ui_resource_request.h b/cc/resources/ui_resource_request.h
index 6d89761..b89e567 100644
--- a/cc/resources/ui_resource_request.h
+++ b/cc/resources/ui_resource_request.h
@@ -16,9 +16,9 @@
 class CC_EXPORT UIResourceRequest {
  public:
   enum UIResourceRequestType {
-    UIResourceCreate,
-    UIResourceDelete,
-    UIResourceInvalidRequest
+    UI_RESOURCE_CREATE,
+    UI_RESOURCE_DELETE,
+    UI_RESOURCE_INVALID_REQUEST
   };
 
   UIResourceRequest(UIResourceRequestType type, UIResourceId id);
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index 0e772eb..1b089df 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/trace_event/trace_event.h"
+#include "cc/base/util.h"
 #include "cc/output/gl_renderer.h"
 #include "cc/resources/resource_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -95,9 +96,9 @@
   // TODO(danakj): Abstract out hw/sw resource create/delete from
   // ResourceProvider and stop using ResourceProvider in this class.
   const ResourceProvider::ResourceId resource_id =
-      resource_provider_->CreateResource(plane_size, GL_CLAMP_TO_EDGE,
-                                         ResourceProvider::TextureHintImmutable,
-                                         format);
+      resource_provider_->CreateResource(
+          plane_size, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   if (resource_id == 0)
     return all_resources_.end();
 
@@ -317,14 +318,41 @@
 
     if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), i)) {
       // We need to transfer data from |video_frame| to the plane resource.
-      const uint8_t* input_plane_pixels = video_frame->data(i);
+      // TODO(reveman): Can use GpuMemoryBuffers here to improve performance.
 
-      gfx::Rect image_rect(0, 0, video_frame->stride(i),
-                           plane_resource.resource_size.height());
-      gfx::Rect source_rect(plane_resource.resource_size);
-      resource_provider_->SetPixels(plane_resource.resource_id,
-                                    input_plane_pixels, image_rect, source_rect,
-                                    gfx::Vector2d());
+      // The |resource_size_pixels| is the size of the resource we want to
+      // upload to.
+      gfx::Size resource_size_pixels = plane_resource.resource_size;
+      // The |video_stride_pixels| is the width of the video frame we are
+      // uploading (including non-frame data to fill in the stride).
+      size_t video_stride_pixels = video_frame->stride(i);
+
+      size_t bytes_per_pixel = BitsPerPixel(plane_resource.resource_format) / 8;
+      // Use 4-byte row alignment (OpenGL default) for upload performance.
+      // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
+      size_t upload_image_stride =
+          RoundUp<size_t>(bytes_per_pixel * resource_size_pixels.width(), 4u);
+
+      const uint8_t* pixels;
+      if (upload_image_stride == video_stride_pixels * bytes_per_pixel) {
+        pixels = video_frame->data(i);
+      } else {
+        // Avoid malloc for each frame/plane if possible.
+        size_t needed_size =
+            upload_image_stride * resource_size_pixels.height();
+        if (upload_pixels_.size() < needed_size)
+          upload_pixels_.resize(needed_size);
+        for (int row = 0; row < resource_size_pixels.height(); ++row) {
+          uint8_t* dst = &upload_pixels_[upload_image_stride * row];
+          const uint8_t* src = video_frame->data(i) +
+                               bytes_per_pixel * video_stride_pixels * row;
+          memcpy(dst, src, resource_size_pixels.width() * bytes_per_pixel);
+        }
+        pixels = &upload_pixels_[0];
+      }
+
+      resource_provider_->CopyToResource(plane_resource.resource_id, pixels,
+                                         resource_size_pixels);
       SetPlaneResourceUniqueId(video_frame.get(), i, &plane_resource);
     }
 
diff --git a/cc/resources/video_resource_updater.h b/cc/resources/video_resource_updater.h
index 622fe68..1bccf79 100644
--- a/cc/resources/video_resource_updater.h
+++ b/cc/resources/video_resource_updater.h
@@ -131,6 +131,7 @@
   ContextProvider* context_provider_;
   ResourceProvider* resource_provider_;
   scoped_ptr<media::SkCanvasVideoRenderer> video_renderer_;
+  std::vector<uint8_t> upload_pixels_;
 
   // Recycle resources so that we can reduce the number of allocations and
   // data transfers.
diff --git a/cc/resources/video_resource_updater_unittest.cc b/cc/resources/video_resource_updater_unittest.cc
index 5433bcf..2098357 100644
--- a/cc/resources/video_resource_updater_unittest.cc
+++ b/cc/resources/video_resource_updater_unittest.cc
@@ -38,6 +38,21 @@
   int upload_count_;
 };
 
+class SharedBitmapManagerAllocationCounter : public TestSharedBitmapManager {
+ public:
+  scoped_ptr<SharedBitmap> AllocateSharedBitmap(
+      const gfx::Size& size) override {
+    ++allocation_count_;
+    return TestSharedBitmapManager::AllocateSharedBitmap(size);
+  }
+
+  int AllocationCount() { return allocation_count_; }
+  void ResetAllocationCount() { allocation_count_ = 0; }
+
+ private:
+  int allocation_count_;
+};
+
 class VideoResourceUpdaterTest : public testing::Test {
  protected:
   VideoResourceUpdaterTest() {
@@ -49,7 +64,12 @@
     output_surface3d_ =
         FakeOutputSurface::Create3d(context3d.Pass());
     CHECK(output_surface3d_->BindToClient(&client_));
-    shared_bitmap_manager_.reset(new TestSharedBitmapManager());
+
+    output_surface_software_ = FakeOutputSurface::CreateSoftware(
+        make_scoped_ptr(new SoftwareOutputDevice));
+    CHECK(output_surface_software_->BindToClient(&client_));
+
+    shared_bitmap_manager_.reset(new SharedBitmapManagerAllocationCounter());
     resource_provider3d_ =
         ResourceProvider::Create(output_surface3d_.get(),
                                  shared_bitmap_manager_.get(),
@@ -58,6 +78,10 @@
                                  0,
                                  false,
                                  1);
+
+    resource_provider_software_ = ResourceProvider::Create(
+        output_surface_software_.get(), shared_bitmap_manager_.get(), NULL,
+        NULL, 0, false, 1);
   }
 
   scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame() {
@@ -85,8 +109,10 @@
   WebGraphicsContext3DUploadCounter* context3d_;
   FakeOutputSurfaceClient client_;
   scoped_ptr<FakeOutputSurface> output_surface3d_;
-  scoped_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
+  scoped_ptr<FakeOutputSurface> output_surface_software_;
+  scoped_ptr<SharedBitmapManagerAllocationCounter> shared_bitmap_manager_;
   scoped_ptr<ResourceProvider> resource_provider3d_;
+  scoped_ptr<ResourceProvider> resource_provider_software_;
 };
 
 TEST_F(VideoResourceUpdaterTest, SoftwareFrame) {
@@ -112,6 +138,7 @@
   EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type);
   EXPECT_EQ(size_t(3), resources.mailboxes.size());
   EXPECT_EQ(size_t(3), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(0), resources.software_resources.size());
   // Expect exactly three texture uploads, one for each plane.
   EXPECT_EQ(3, context3d_->UploadCount());
 
@@ -143,6 +170,7 @@
   EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type);
   EXPECT_EQ(size_t(3), resources.mailboxes.size());
   EXPECT_EQ(size_t(3), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(0), resources.software_resources.size());
   // Expect exactly three texture uploads, one for each plane.
   EXPECT_EQ(3, context3d_->UploadCount());
 
@@ -156,5 +184,72 @@
   EXPECT_EQ(0, context3d_->UploadCount());
 }
 
+TEST_F(VideoResourceUpdaterTest, SoftwareFrameSoftwareCompositor) {
+  VideoResourceUpdater updater(nullptr, resource_provider_software_.get());
+  scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
+
+  VideoFrameExternalResources resources =
+      updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+}
+
+TEST_F(VideoResourceUpdaterTest, ReuseResourceSoftwareCompositor) {
+  VideoResourceUpdater updater(nullptr, resource_provider_software_.get());
+  scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
+  video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234));
+
+  // Allocate the resources for a software video frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  VideoFrameExternalResources resources =
+      updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // Expect exactly one allocated shared bitmap.
+  EXPECT_EQ(1, shared_bitmap_manager_->AllocationCount());
+
+  // Simulate the ResourceProvider releasing the resource back to the video
+  // updater.
+  resources.software_release_callback.Run(0, false, nullptr);
+
+  // Allocate resources for the same frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  resources = updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // The data should be reused so expect no new allocations.
+  EXPECT_EQ(0, shared_bitmap_manager_->AllocationCount());
+}
+
+TEST_F(VideoResourceUpdaterTest, ReuseResourceNoDeleteSoftwareCompositor) {
+  VideoResourceUpdater updater(nullptr, resource_provider_software_.get());
+  scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
+  video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234));
+
+  // Allocate the resources for a software video frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  VideoFrameExternalResources resources =
+      updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // Expect exactly one allocated shared bitmap.
+  EXPECT_EQ(1, shared_bitmap_manager_->AllocationCount());
+
+  // Allocate resources for the same frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  resources = updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // The data should be reused so expect no new allocations.
+  EXPECT_EQ(0, shared_bitmap_manager_->AllocationCount());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index ed8c0a2..904a057 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -335,6 +335,9 @@
   if (state_machine_.ShouldSetNeedsBeginFrames(
           frame_source_->NeedsBeginFrames())) {
     frame_source_->SetNeedsBeginFrames(state_machine_.BeginFrameNeeded());
+    if (!frame_source_->NeedsBeginFrames()) {
+      client_->SendBeginMainFrameNotExpectedSoon();
+    }
   }
 
   if (state_machine_.begin_impl_frame_state() ==
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 82f305a..370ac25 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -49,6 +49,7 @@
   virtual base::TimeDelta CommitToActivateDurationEstimate() = 0;
   virtual void DidBeginImplFrameDeadline() = 0;
   virtual void SendBeginFramesToChildren(const BeginFrameArgs& args) = 0;
+  virtual void SendBeginMainFrameNotExpectedSoon() = 0;
 
  protected:
   virtual ~SchedulerClient() {}
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index ac52271..50cc846 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -161,6 +161,10 @@
     begin_frame_is_sent_to_children_ = true;
   }
 
+  void SendBeginMainFrameNotExpectedSoon() override {
+    PushAction("SendBeginMainFrameNotExpectedSoon");
+  }
+
   base::Callback<bool(void)> ImplFrameDeadlinePending(bool state) {
     return base::Bind(&FakeSchedulerClient::ImplFrameDeadlinePendingCallback,
                       base::Unretained(this),
@@ -517,7 +521,8 @@
   client_->Reset();
 
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   client_->Reset();
 }
 
@@ -1001,7 +1006,8 @@
   EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
   client->Reset();
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
   EXPECT_EQ(0, client->num_draws());
 
@@ -1376,7 +1382,8 @@
   client_->Reset();
 
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   client_->Reset();
 }
 
@@ -1627,7 +1634,7 @@
   // Make sure SetNeedsBeginFrame isn't called on the client
   // when the BeginFrame is no longer needed.
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_NO_ACTION(client_);
+  EXPECT_SINGLE_ACTION("SendBeginMainFrameNotExpectedSoon", client_);
   client_->Reset();
 }
 
@@ -1783,7 +1790,8 @@
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
   // Do nothing when impl frame is in deadine pending state.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
 
   client_->Reset();
   scheduler_->NotifyBeginMainFrameStarted();
@@ -1814,7 +1822,8 @@
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
   // Do nothing when impl frame is in deadine pending state.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
 
   client_->Reset();
   // Run posted deadline.
@@ -1881,10 +1890,12 @@
   scheduler_->DidLoseOutputSurface();
   if (impl_side_painting) {
     // Sync tree should be forced to activate.
-    EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 2);
-    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 2);
+    EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 3);
+    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+    EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
   } else {
-    EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+    EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   }
 
   client_->Reset();
@@ -1916,7 +1927,8 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
 
   client_->Reset();
   task_runner().RunPendingTasks();  // Run posted deadline.
@@ -1967,8 +1979,9 @@
   client_->Reset();
   EXPECT_FALSE(scheduler_->IsBeginRetroFrameArgsEmpty());
   scheduler_->DidLoseOutputSurface();
-  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 2);
-  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 2);
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
   EXPECT_TRUE(scheduler_->IsBeginRetroFrameArgsEmpty());
 
   // Posted BeginRetroFrame is aborted.
@@ -2028,7 +2041,8 @@
   client_->Reset();
   EXPECT_FALSE(scheduler_->IsBeginRetroFrameArgsEmpty());
   scheduler_->DidLoseOutputSurface();
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   EXPECT_TRUE(scheduler_->IsBeginRetroFrameArgsEmpty());
 
   // BeginImplFrame deadline should abort drawing.
@@ -2069,7 +2083,7 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  EXPECT_NO_ACTION(client_);
+  EXPECT_SINGLE_ACTION("SendBeginMainFrameNotExpectedSoon", client_);
   EXPECT_FALSE(scheduler_->frame_source().NeedsBeginFrames());
 
   client_->Reset();
@@ -2347,5 +2361,40 @@
   EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1);
 }
 
+// Tests to ensure that we send a BeginMainFrameNotExpectedSoon when expected.
+TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon) {
+  scheduler_settings_.use_external_begin_frame_source = true;
+  SetUpScheduler(true);
+
+  // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+  scheduler_->SetNeedsCommit();
+  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+  client_->Reset();
+
+  // Trigger a frame draw.
+  EXPECT_SCOPED(AdvanceFrame());
+  scheduler_->NotifyBeginMainFrameStarted();
+  scheduler_->NotifyReadyToCommit();
+  task_runner().RunPendingTasks();
+  EXPECT_ACTION("WillBeginImplFrame", client_, 0, 5);
+  EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 5);
+  EXPECT_ACTION("ScheduledActionCommit", client_, 2, 5);
+  EXPECT_ACTION("ScheduledActionAnimate", client_, 3, 5);
+  EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 5);
+  client_->Reset();
+
+  // The following BeginImplFrame deadline should SetNeedsBeginFrame(false)
+  // and send a SendBeginMainFrameNotExpectedSoon.
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_);
+  EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+  client_->Reset();
+
+  task_runner().RunPendingTasks();  // Run posted deadline.
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+  client_->Reset();
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/surfaces/display.cc b/cc/surfaces/display.cc
index ab1f0af..7ec4672 100644
--- a/cc/surfaces/display.cc
+++ b/cc/surfaces/display.cc
@@ -134,25 +134,46 @@
   }
   DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
 
-  gfx::Size surface_size =
-      frame_data->render_pass_list.back()->output_rect.size();
+  frame->metadata.latency_info.insert(frame->metadata.latency_info.end(),
+                                      stored_latency_info_.begin(),
+                                      stored_latency_info_.end());
+  stored_latency_info_.clear();
+  bool have_copy_requests = false;
+  for (const auto* pass : frame_data->render_pass_list) {
+    have_copy_requests |= !pass->copy_requests.empty();
+  }
 
-  gfx::Rect device_viewport_rect = gfx::Rect(current_surface_size_);
-  gfx::Rect device_clip_rect = device_viewport_rect;
-  bool disable_picture_quad_image_filtering = false;
+  gfx::Size surface_size;
+  bool have_damage = false;
+  if (!frame_data->render_pass_list.empty()) {
+    surface_size = frame_data->render_pass_list.back()->output_rect.size();
+    have_damage =
+        !frame_data->render_pass_list.back()->damage_rect.size().IsEmpty();
+  }
+  bool avoid_swap = surface_size != current_surface_size_;
+  bool should_draw = !frame->metadata.latency_info.empty() ||
+                     have_copy_requests || (have_damage && !avoid_swap);
 
-  renderer_->DecideRenderPassAllocationsForFrame(frame_data->render_pass_list);
-  renderer_->DrawFrame(&frame_data->render_pass_list,
-                       device_scale_factor_,
-                       device_viewport_rect,
-                       device_clip_rect,
-                       disable_picture_quad_image_filtering);
+  if (should_draw) {
+    gfx::Rect device_viewport_rect = gfx::Rect(current_surface_size_);
+    gfx::Rect device_clip_rect = device_viewport_rect;
+    bool disable_picture_quad_image_filtering = false;
 
-  if (surface_size != current_surface_size_) {
+    renderer_->DecideRenderPassAllocationsForFrame(
+        frame_data->render_pass_list);
+    renderer_->DrawFrame(&frame_data->render_pass_list, device_scale_factor_,
+                         device_viewport_rect, device_clip_rect,
+                         disable_picture_quad_image_filtering);
+  }
+
+  if (should_draw && !avoid_swap) {
+    renderer_->SwapBuffers(frame->metadata);
+  } else {
+    stored_latency_info_.insert(stored_latency_info_.end(),
+                                frame->metadata.latency_info.begin(),
+                                frame->metadata.latency_info.end());
     DidSwapBuffers();
     DidSwapBuffersComplete();
-  } else {
-    renderer_->SwapBuffers(frame->metadata);
   }
 
   return true;
diff --git a/cc/surfaces/display.h b/cc/surfaces/display.h
index 99e66fb..ed96d07 100644
--- a/cc/surfaces/display.h
+++ b/cc/surfaces/display.h
@@ -5,6 +5,8 @@
 #ifndef CC_SURFACES_DISPLAY_H_
 #define CC_SURFACES_DISPLAY_H_
 
+#include <vector>
+
 #include "base/memory/scoped_ptr.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/renderer.h"
@@ -101,6 +103,7 @@
   scoped_ptr<DirectRenderer> renderer_;
   scoped_ptr<BlockingTaskRunner> blocking_main_thread_task_runner_;
   scoped_ptr<TextureMailboxDeleter> texture_mailbox_deleter_;
+  std::vector<ui::LatencyInfo> stored_latency_info_;
 
   DISALLOW_COPY_AND_ASSIGN(Display);
 };
diff --git a/cc/surfaces/display_unittest.cc b/cc/surfaces/display_unittest.cc
new file mode 100644
index 0000000..7b01244
--- /dev/null
+++ b/cc/surfaces/display_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/compositor_frame.h"
+#include "cc/output/copy_output_result.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass.h"
+#include "cc/resources/shared_bitmap_manager.h"
+#include "cc/surfaces/display.h"
+#include "cc/surfaces/display_client.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class EmptySurfaceFactoryClient : public SurfaceFactoryClient {
+ public:
+  void ReturnResources(const ReturnedResourceArray& resources) override {}
+};
+
+class DisplayTest : public testing::Test {
+ public:
+  DisplayTest() : factory_(&manager_, &empty_client_) {}
+
+  void SetUp() override {
+    output_surface_ = FakeOutputSurface::CreateSoftware(
+        make_scoped_ptr(new SoftwareOutputDevice));
+    shared_bitmap_manager_.reset(new TestSharedBitmapManager);
+    output_surface_ptr_ = output_surface_.get();
+  }
+
+ protected:
+  void SubmitFrame(RenderPassList* pass_list, SurfaceId surface_id) {
+    scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
+    pass_list->swap(frame_data->render_pass_list);
+
+    scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+    frame->delegated_frame_data = frame_data.Pass();
+
+    factory_.SubmitFrame(surface_id, frame.Pass(),
+                         SurfaceFactory::DrawCallback());
+  }
+
+  SurfaceManager manager_;
+  EmptySurfaceFactoryClient empty_client_;
+  SurfaceFactory factory_;
+  scoped_ptr<FakeOutputSurface> output_surface_;
+  FakeOutputSurface* output_surface_ptr_;
+  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
+};
+
+class TestDisplayClient : public DisplayClient {
+ public:
+  TestDisplayClient() : damaged(false), swapped(false) {}
+  ~TestDisplayClient() override {}
+
+  void DisplayDamaged() override { damaged = true; }
+  void DidSwapBuffers() override { swapped = true; }
+  void DidSwapBuffersComplete() override {}
+  void CommitVSyncParameters(base::TimeTicks timebase,
+                             base::TimeDelta interval) override {}
+  void OutputSurfaceLost() override {}
+  void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override {}
+
+  bool damaged;
+  bool swapped;
+};
+
+void CopyCallback(bool* called, scoped_ptr<CopyOutputResult> result) {
+  *called = true;
+}
+
+// Check that frame is damaged and swapped only under correct conditions.
+TEST_F(DisplayTest, DisplayDamaged) {
+  TestDisplayClient client;
+  RendererSettings settings;
+  settings.partial_swap_enabled = true;
+  Display display(&client, &manager_, shared_bitmap_manager_.get(), nullptr,
+                  settings);
+
+  display.Initialize(output_surface_.Pass());
+
+  SurfaceId surface_id(7u);
+  EXPECT_FALSE(client.damaged);
+  display.SetSurfaceId(surface_id, 1.f);
+  EXPECT_TRUE(client.damaged);
+
+  client.damaged = false;
+  display.Resize(gfx::Size(100, 100));
+  EXPECT_TRUE(client.damaged);
+
+  factory_.Create(surface_id);
+
+  // First draw from surface should have full damage.
+  RenderPassList pass_list;
+  scoped_ptr<RenderPass> pass = RenderPass::Create();
+  pass->output_rect = gfx::Rect(0, 0, 100, 100);
+  pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+  pass->id = RenderPassId(1, 1);
+  pass_list.push_back(pass.Pass());
+
+  client.damaged = false;
+  SubmitFrame(&pass_list, surface_id);
+  EXPECT_TRUE(client.damaged);
+
+  EXPECT_FALSE(client.swapped);
+  EXPECT_EQ(0u, output_surface_ptr_->num_sent_frames());
+  display.Draw();
+  EXPECT_TRUE(client.swapped);
+  EXPECT_EQ(1u, output_surface_ptr_->num_sent_frames());
+  SoftwareFrameData* software_data =
+      output_surface_ptr_->last_sent_frame().software_frame_data.get();
+  ASSERT_NE(nullptr, software_data);
+  EXPECT_EQ(gfx::Size(100, 100).ToString(), software_data->size.ToString());
+  EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
+            software_data->damage_rect.ToString());
+
+  {
+    // Only damaged portion should be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(2u, output_surface_ptr_->num_sent_frames());
+    software_data =
+        output_surface_ptr_->last_sent_frame().software_frame_data.get();
+    ASSERT_NE(nullptr, software_data);
+    EXPECT_EQ(gfx::Size(100, 100).ToString(), software_data->size.ToString());
+    EXPECT_EQ(gfx::Rect(10, 10, 1, 1).ToString(),
+              software_data->damage_rect.ToString());
+  }
+
+  {
+    // Pass has no damage so shouldn't be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(2u, output_surface_ptr_->num_sent_frames());
+  }
+
+  {
+    // Pass is wrong size so shouldn't be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 99, 99);
+    pass->damage_rect = gfx::Rect(10, 10, 10, 10);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(2u, output_surface_ptr_->num_sent_frames());
+  }
+
+  {
+    // Pass has copy output request so should be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+    bool copy_called = false;
+    pass->copy_requests.push_back(CopyOutputRequest::CreateRequest(
+        base::Bind(&CopyCallback, &copy_called)));
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(3u, output_surface_ptr_->num_sent_frames());
+    EXPECT_TRUE(copy_called);
+  }
+
+  // Pass has latency info so should be swapped.
+  {
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
+    pass_list.swap(frame_data->render_pass_list);
+
+    scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+    frame->delegated_frame_data = frame_data.Pass();
+    frame->metadata.latency_info.push_back(ui::LatencyInfo());
+
+    factory_.SubmitFrame(surface_id, frame.Pass(),
+                         SurfaceFactory::DrawCallback());
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(4u, output_surface_ptr_->num_sent_frames());
+  }
+
+  factory_.Destroy(surface_id);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc
index f6179cc..d04c5d3 100644
--- a/cc/test/animation_test_common.cc
+++ b/cc/test/animation_test_common.cc
@@ -44,10 +44,8 @@
   int id = AnimationIdProvider::NextAnimationId();
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(),
-                        id,
-                        AnimationIdProvider::NextGroupId(),
-                        Animation::Opacity));
+      Animation::Create(curve.Pass(), id, AnimationIdProvider::NextGroupId(),
+                        Animation::OPACITY));
   animation->set_needs_synchronized_start_time(true);
 
   target->AddAnimation(animation.Pass());
@@ -73,10 +71,8 @@
   int id = AnimationIdProvider::NextAnimationId();
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(),
-                        id,
-                        AnimationIdProvider::NextGroupId(),
-                        Animation::Transform));
+      Animation::Create(curve.Pass(), id, AnimationIdProvider::NextGroupId(),
+                        Animation::TRANSFORM));
   animation->set_needs_synchronized_start_time(true);
 
   target->AddAnimation(animation.Pass());
@@ -122,7 +118,7 @@
   int id = AnimationIdProvider::NextAnimationId();
 
   scoped_ptr<Animation> animation(Animation::Create(
-      curve.Pass(), id, AnimationIdProvider::NextGroupId(), Animation::Filter));
+      curve.Pass(), id, AnimationIdProvider::NextGroupId(), Animation::FILTER));
   animation->set_needs_synchronized_start_time(true);
 
   target->AddAnimation(animation.Pass());
diff --git a/cc/test/data/blue.png b/cc/test/data/blue.png
deleted file mode 100644
index 48a2680..0000000
--- a/cc/test/data/blue.png
+++ /dev/null
Binary files differ
diff --git a/cc/test/fake_content_layer_client.cc b/cc/test/fake_content_layer_client.cc
index 23b73f5..ab958e9 100644
--- a/cc/test/fake_content_layer_client.cc
+++ b/cc/test/fake_content_layer_client.cc
@@ -69,13 +69,12 @@
        it != draw_rects_.end(); ++it) {
     const gfx::RectF& draw_rect = it->first;
     const SkPaint& paint = it->second;
-    canvas = skia::SharePtr(
-        recorder.beginRecording(draw_rect.width(), draw_rect.height()));
+    canvas =
+        skia::SharePtr(recorder.beginRecording(gfx::RectFToSkRect(draw_rect)));
     canvas->drawRectCoords(0.f, 0.f, draw_rect.width(), draw_rect.height(),
                            paint);
     picture = skia::AdoptRef(recorder.endRecording());
-    list->AppendItem(DrawingDisplayItem::Create(
-        picture, gfx::PointF(draw_rect.x(), draw_rect.y())));
+    list->AppendItem(DrawingDisplayItem::Create(picture));
   }
 
   for (BitmapVector::const_iterator it = draw_bitmaps_.begin();
@@ -84,8 +83,7 @@
         recorder.beginRecording(it->bitmap.width(), it->bitmap.height()));
     canvas->drawBitmap(it->bitmap, 0.f, 0.f, &it->paint);
     picture = skia::AdoptRef(recorder.endRecording());
-    list->AppendItem(DrawingDisplayItem::Create(
-        picture, gfx::PointF(it->point.x(), it->point.y())));
+    list->AppendItem(DrawingDisplayItem::Create(picture));
   }
 
   if (fill_with_nonsolid_color_) {
@@ -94,11 +92,11 @@
     while (!draw_rect.IsEmpty()) {
       SkPaint paint;
       paint.setColor(red ? SK_ColorRED : SK_ColorBLUE);
-      canvas =
-          skia::SharePtr(recorder.beginRecording(clip.width(), clip.height()));
+      canvas = skia::SharePtr(
+          recorder.beginRecording(gfx::RectFToSkRect(draw_rect)));
       canvas->drawRect(gfx::RectFToSkRect(draw_rect), paint);
       picture = skia::AdoptRef(recorder.endRecording());
-      list->AppendItem(DrawingDisplayItem::Create(picture, gfx::PointF()));
+      list->AppendItem(DrawingDisplayItem::Create(picture));
       draw_rect.Inset(1, 1);
     }
   }
diff --git a/cc/test/fake_layer_tree_host_client.h b/cc/test/fake_layer_tree_host_client.h
index f5f3504..b5093ad 100644
--- a/cc/test/fake_layer_tree_host_client.h
+++ b/cc/test/fake_layer_tree_host_client.h
@@ -33,6 +33,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override {}
   void BeginMainFrame(const BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void Layout() override {}
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc
index bfac501..ccfb203 100644
--- a/cc/test/fake_layer_tree_host_impl.cc
+++ b/cc/test/fake_layer_tree_host_impl.cc
@@ -86,7 +86,8 @@
 void FakeLayerTreeHostImpl::UpdateNumChildrenAndDrawProperties(
     LayerTreeImpl* layerTree) {
   RecursiveUpdateNumChildren(layerTree->root_layer());
-  layerTree->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  layerTree->UpdateDrawProperties(update_lcd_text);
 }
 
 }  // namespace cc
diff --git a/cc/test/fake_picture_pile.cc b/cc/test/fake_picture_pile.cc
index cc0a7ba..4db6c58 100644
--- a/cc/test/fake_picture_pile.cc
+++ b/cc/test/fake_picture_pile.cc
@@ -49,7 +49,8 @@
   return CreatePile(tile_size, layer_bounds, is_filled);
 }
 
-scoped_refptr<RasterSource> FakePicturePile::CreateRasterSource() const {
+scoped_refptr<RasterSource> FakePicturePile::CreateRasterSource(
+    bool can_use_lcd_text) const {
   return FakePicturePileImpl::CreateFromPile(this, playback_allowed_event_);
 }
 
diff --git a/cc/test/fake_picture_pile.h b/cc/test/fake_picture_pile.h
index 054eaa2..48b8914 100644
--- a/cc/test/fake_picture_pile.h
+++ b/cc/test/fake_picture_pile.h
@@ -34,7 +34,8 @@
       const gfx::Size& layer_bounds);
 
   // PicturePile overrides.
-  scoped_refptr<RasterSource> CreateRasterSource() const override;
+  scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const override;
 
   using PicturePile::buffer_pixels;
   using PicturePile::CanRasterSlowTileCheck;
diff --git a/cc/test/fake_picture_pile_impl.cc b/cc/test/fake_picture_pile_impl.cc
index 8353d27..ab255cc 100644
--- a/cc/test/fake_picture_pile_impl.cc
+++ b/cc/test/fake_picture_pile_impl.cc
@@ -21,7 +21,7 @@
 FakePicturePileImpl::FakePicturePileImpl(
     const PicturePile* other,
     base::WaitableEvent* playback_allowed_event)
-    : PicturePileImpl(other),
+    : PicturePileImpl(other, true),
       playback_allowed_event_(playback_allowed_event),
       tile_grid_size_(other->GetTileGridSizeForTesting()) {
 }
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index efe70a0..b0b81f7 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -156,11 +156,11 @@
     for (size_t i = 0; i < list->GetSize(); i++) {
       success &= list->GetString(i, &str);
       if (str == "StartTouch")
-        blocks |= ScrollBlocksOnStartTouch;
+        blocks |= SCROLL_BLOCKS_ON_START_TOUCH;
       else if (str == "WheelEvent")
-        blocks |= ScrollBlocksOnWheelEvent;
+        blocks |= SCROLL_BLOCKS_ON_WHEEL_EVENT;
       else if (str == "ScrollEvent")
-        blocks |= ScrollBlocksOnScrollEvent;
+        blocks |= SCROLL_BLOCKS_ON_SCROLL_EVENT;
       else
         success = false;
     }
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc
index f7ab9c2..dbb9ba5 100644
--- a/cc/test/layer_tree_pixel_test.cc
+++ b/cc/test/layer_tree_pixel_test.cc
@@ -63,10 +63,8 @@
   return output_surface.Pass();
 }
 
-void LayerTreePixelTest::CommitCompleteOnThread(LayerTreeHostImpl* impl) {
-  LayerTreeImpl* commit_tree =
-      impl->pending_tree() ? impl->pending_tree() : impl->active_tree();
-  if (commit_tree->source_frame_number() != 0)
+void LayerTreePixelTest::WillActivateTreeOnThread(LayerTreeHostImpl* impl) {
+  if (impl->sync_tree()->source_frame_number() != 0)
     return;
 
   DirectRenderer* renderer = static_cast<DirectRenderer*>(impl->renderer());
diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h
index 8d02280..da030d1 100644
--- a/cc/test/layer_tree_pixel_test.h
+++ b/cc/test/layer_tree_pixel_test.h
@@ -41,7 +41,7 @@
   ~LayerTreePixelTest() override;
 
   scoped_ptr<OutputSurface> CreateOutputSurface() override;
-  void CommitCompleteOnThread(LayerTreeHostImpl* impl) override;
+  void WillActivateTreeOnThread(LayerTreeHostImpl* impl) override;
 
   virtual scoped_ptr<CopyOutputRequest> CreateCopyOutputRequest();
 
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index ce69079..129a27d 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -441,6 +441,7 @@
   void DidAbortSwapBuffers() override {}
   void ScheduleComposite() override { test_hooks_->ScheduleComposite(); }
   void DidCompletePageScaleAnimation() override {}
+  void BeginMainFrameNotExpectedSoon() override {}
 
  private:
   explicit LayerTreeHostClientForTesting(TestHooks* test_hooks)
diff --git a/cc/test/render_pass_test_common.cc b/cc/test/render_pass_test_common.cc
index 7c91f0d..0e3fe22 100644
--- a/cc/test/render_pass_test_common.cc
+++ b/cc/test/render_pass_test_common.cc
@@ -35,46 +35,39 @@
   const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
 
   ResourceProvider::ResourceId resource1 = resource_provider->CreateResource(
-      gfx::Size(45, 5),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(45, 5), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource1);
   ResourceProvider::ResourceId resource2 = resource_provider->CreateResource(
-      gfx::Size(346, 61),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(346, 61), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource2);
   ResourceProvider::ResourceId resource3 = resource_provider->CreateResource(
-      gfx::Size(12, 134),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(12, 134), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource3);
   ResourceProvider::ResourceId resource4 = resource_provider->CreateResource(
-      gfx::Size(56, 12),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(56, 12), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource4);
   gfx::Size resource5_size(73, 26);
   ResourceProvider::ResourceId resource5 = resource_provider->CreateResource(
-      resource5_size,
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      resource5_size, GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource5);
   ResourceProvider::ResourceId resource6 = resource_provider->CreateResource(
-      gfx::Size(64, 92),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(64, 92), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource6);
   ResourceProvider::ResourceId resource7 = resource_provider->CreateResource(
-      gfx::Size(9, 14),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(9, 14), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource7);
 
@@ -243,9 +236,8 @@
   ResourceProvider::ResourceId plane_resources[4];
   for (int i = 0; i < 4; ++i) {
     plane_resources[i] = resource_provider->CreateResource(
-        gfx::Size(20, 12),
-        GL_CLAMP_TO_EDGE,
-        ResourceProvider::TextureHintImmutable,
+        gfx::Size(20, 12), GL_CLAMP_TO_EDGE,
+        ResourceProvider::TEXTURE_HINT_IMMUTABLE,
         resource_provider->best_texture_format());
     resource_provider->AllocateForTesting(plane_resources[i]);
   }
diff --git a/cc/test/test_web_graphics_context_3d.cc b/cc/test/test_web_graphics_context_3d.cc
index 8aa392d..28b16c8 100644
--- a/cc/test/test_web_graphics_context_3d.cc
+++ b/cc/test/test_web_graphics_context_3d.cc
@@ -64,7 +64,7 @@
       height_(0),
       scale_factor_(-1.f),
       test_support_(NULL),
-      last_update_type_(NoUpdate),
+      last_update_type_(NO_UPDATE),
       next_insert_sync_point_(1),
       last_waited_sync_point_(0),
       unpack_alignment_(4),
diff --git a/cc/test/test_web_graphics_context_3d.h b/cc/test/test_web_graphics_context_3d.h
index 31182f8..c2dee45 100644
--- a/cc/test/test_web_graphics_context_3d.h
+++ b/cc/test/test_web_graphics_context_3d.h
@@ -373,11 +373,7 @@
   void clear_reshape_called() { reshape_called_ = false; }
   float scale_factor() const { return scale_factor_; }
 
-  enum UpdateType {
-    NoUpdate = 0,
-    PrepareTexture,
-    PostSubBuffer
-  };
+  enum UpdateType { NO_UPDATE = 0, PREPARE_TEXTURE, POST_SUB_BUFFER };
 
   gfx::Rect update_rect() const { return update_rect_; }
 
diff --git a/cc/trees/layer_sorter.cc b/cc/trees/layer_sorter.cc
index e171fe0..bde4920 100644
--- a/cc/trees/layer_sorter.cc
+++ b/cc/trees/layer_sorter.cc
@@ -90,7 +90,7 @@
 
   // Early out if the projected bounds don't overlap.
   if (!a->projected_bounds.Intersects(b->projected_bounds))
-    return None;
+    return NONE;
 
   gfx::PointF aPoints[4] = { a->projected_quad.p1(),
                              a->projected_quad.p2(),
@@ -123,7 +123,7 @@
         overlap_points.push_back(r);
 
   if (overlap_points.empty())
-    return None;
+    return NONE;
 
   // Check the corresponding layer depth value for all overlap points to
   // determine which layer is in front.
@@ -157,7 +157,7 @@
 
   // If we can't tell which should come first, we use document order.
   if (!accurate)
-    return ABeforeB;
+    return A_BEFORE_B;
 
   float max_diff =
       std::abs(max_positive) > std::abs(max_negative) ?
@@ -176,9 +176,9 @@
   // Maintain relative order if the layers have the same depth at all
   // intersection points.
   if (max_diff <= 0.f)
-    return ABeforeB;
+    return A_BEFORE_B;
 
-  return BBeforeA;
+  return B_BEFORE_A;
 }
 
 LayerShape::LayerShape() {}
@@ -320,10 +320,10 @@
                                                     &weight);
       GraphNode* start_node = NULL;
       GraphNode* end_node = NULL;
-      if (overlap_result == ABeforeB) {
+      if (overlap_result == A_BEFORE_B) {
         start_node = &node_a;
         end_node = &node_b;
-      } else if (overlap_result == BBeforeA) {
+      } else if (overlap_result == B_BEFORE_A) {
         start_node = &node_b;
         end_node = &node_a;
       }
diff --git a/cc/trees/layer_sorter.h b/cc/trees/layer_sorter.h
index b783ded..4cfa8fe 100644
--- a/cc/trees/layer_sorter.h
+++ b/cc/trees/layer_sorter.h
@@ -68,11 +68,7 @@
 
   void Sort(LayerImplList::iterator first, LayerImplList::iterator last);
 
-  enum ABCompareResult {
-    ABeforeB,
-    BBeforeA,
-    None
-  };
+  enum ABCompareResult { A_BEFORE_B, B_BEFORE_A, NONE };
 
   static ABCompareResult CheckOverlap(LayerShape* a,
                                       LayerShape* b,
diff --git a/cc/trees/layer_sorter_unittest.cc b/cc/trees/layer_sorter_unittest.cc
index ba7765b..74d3cf5 100644
--- a/cc/trees/layer_sorter_unittest.cc
+++ b/cc/trees/layer_sorter_unittest.cc
@@ -37,12 +37,12 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&front, &back, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::BBeforeA, overlap_result);
+  EXPECT_EQ(LayerSorter::B_BEFORE_A, overlap_result);
   EXPECT_EQ(1.f, weight);
 
   overlap_result =
       LayerSorter::CheckOverlap(&back, &front, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
   EXPECT_EQ(1.f, weight);
 
   // One layer translated off to the right. No overlap should be detected.
@@ -51,7 +51,7 @@
   LayerShape back_right(2.f, 2.f, right_translate);
   overlap_result =
       LayerSorter::CheckOverlap(&front, &back_right, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::None, overlap_result);
+  EXPECT_EQ(LayerSorter::NONE, overlap_result);
 
   // When comparing a layer with itself, z difference is always 0.
   overlap_result =
@@ -80,7 +80,7 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&front_face, &left_face, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::BBeforeA, overlap_result);
+  EXPECT_EQ(LayerSorter::B_BEFORE_A, overlap_result);
 }
 
 TEST(LayerSorterTest, IntersectingLayerOverlap) {
@@ -107,7 +107,7 @@
                                              &rotated_face,
                                              z_threshold,
                                              &weight);
-  EXPECT_NE(LayerSorter::None, overlap_result);
+  EXPECT_NE(LayerSorter::NONE, overlap_result);
   EXPECT_EQ(0.f, weight);
 }
 
@@ -145,13 +145,13 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&layer_a, &layer_c, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
   overlap_result =
       LayerSorter::CheckOverlap(&layer_c, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
   overlap_result =
       LayerSorter::CheckOverlap(&layer_a, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::None, overlap_result);
+  EXPECT_EQ(LayerSorter::NONE, overlap_result);
 }
 
 TEST(LayerSorterTest, LayersUnderPathologicalPerspectiveTransform) {
@@ -192,7 +192,7 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&layer_a, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
 }
 
 TEST(LayerSorterTest, VerifyExistingOrderingPreservedWhenNoZDiff) {
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index f478078..49e374d 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -133,7 +133,6 @@
       partial_texture_update_requests_(0),
       did_complete_scale_animation_(false),
       in_paint_layer_contents_(false),
-      total_frames_used_for_lcd_text_metrics_(0),
       id_(s_layer_tree_host_sequence_number.GetNext() + 1),
       next_commit_forces_redraw_(false),
       shared_bitmap_manager_(shared_bitmap_manager),
@@ -233,6 +232,10 @@
   client_->DidBeginMainFrame();
 }
 
+void LayerTreeHost::BeginMainFrameNotExpectedSoon() {
+  client_->BeginMainFrameNotExpectedSoon();
+}
+
 void LayerTreeHost::BeginMainFrame(const BeginFrameArgs& args) {
   inside_begin_main_frame_ = true;
   client_->BeginMainFrame(args);
@@ -559,19 +562,19 @@
         animation_controllers.find(event_layer_id);
     if (iter != animation_controllers.end()) {
       switch ((*events)[event_index].type) {
-        case AnimationEvent::Started:
+        case AnimationEvent::STARTED:
           (*iter).second->NotifyAnimationStarted((*events)[event_index]);
           break;
 
-        case AnimationEvent::Finished:
+        case AnimationEvent::FINISHED:
           (*iter).second->NotifyAnimationFinished((*events)[event_index]);
           break;
 
-        case AnimationEvent::Aborted:
+        case AnimationEvent::ABORTED:
           (*iter).second->NotifyAnimationAborted((*events)[event_index]);
           break;
 
-        case AnimationEvent::PropertyUpdate:
+        case AnimationEvent::PROPERTY_UPDATE:
           (*iter).second->NotifyAnimationPropertyUpdate((*events)[event_index]);
           break;
       }
@@ -809,18 +812,6 @@
   gpu_rasterization_histogram_recorded_ = true;
 }
 
-void LayerTreeHost::CalculateLCDTextMetricsCallback(Layer* layer) {
-  if (!layer->SupportsLCDText())
-    return;
-
-  lcd_text_metrics_.total_num_cc_layers++;
-  if (layer->draw_properties().can_use_lcd_text) {
-    lcd_text_metrics_.total_num_cc_layers_can_use_lcd_text++;
-    if (layer->contents_opaque())
-      lcd_text_metrics_.total_num_cc_layers_will_use_lcd_text++;
-  }
-}
-
 bool LayerTreeHost::UsingSharedMemoryResources() {
   return GetRendererCapabilities().using_shared_memory_resources;
 }
@@ -861,29 +852,6 @@
         settings_.verify_property_trees, &update_list,
         render_surface_layer_list_id);
     LayerTreeHostCommon::CalculateDrawProperties(&inputs);
-
-    if (total_frames_used_for_lcd_text_metrics_ <=
-        kTotalFramesToUseForLCDTextMetrics) {
-      LayerTreeHostCommon::CallFunctionForSubtree(
-          root_layer,
-          base::Bind(&LayerTreeHost::CalculateLCDTextMetricsCallback,
-                     base::Unretained(this)));
-      total_frames_used_for_lcd_text_metrics_++;
-    }
-
-    if (total_frames_used_for_lcd_text_metrics_ ==
-        kTotalFramesToUseForLCDTextMetrics) {
-      total_frames_used_for_lcd_text_metrics_++;
-
-      UMA_HISTOGRAM_PERCENTAGE(
-          "Renderer4.LCDText.PercentageOfCandidateLayers",
-          lcd_text_metrics_.total_num_cc_layers_can_use_lcd_text * 100.0 /
-          lcd_text_metrics_.total_num_cc_layers);
-      UMA_HISTOGRAM_PERCENTAGE(
-          "Renderer4.LCDText.PercentageOfAALayers",
-          lcd_text_metrics_.total_num_cc_layers_will_use_lcd_text * 100.0 /
-          lcd_text_metrics_.total_num_cc_layers_can_use_lcd_text);
-    }
   }
 
   // Reset partial texture update requests.
@@ -1251,8 +1219,7 @@
          ui_resource_client_map_.end());
 
   bool resource_lost = false;
-  UIResourceRequest request(UIResourceRequest::UIResourceCreate,
-                            next_id,
+  UIResourceRequest request(UIResourceRequest::UI_RESOURCE_CREATE, next_id,
                             client->GetBitmap(next_id, resource_lost));
   ui_resource_request_queue_.push_back(request);
 
@@ -1270,7 +1237,7 @@
   if (iter == ui_resource_client_map_.end())
     return;
 
-  UIResourceRequest request(UIResourceRequest::UIResourceDelete, uid);
+  UIResourceRequest request(UIResourceRequest::UI_RESOURCE_DELETE, uid);
   ui_resource_request_queue_.push_back(request);
   ui_resource_client_map_.erase(iter);
 }
@@ -1282,8 +1249,7 @@
     UIResourceId uid = iter->first;
     const UIResourceClientData& data = iter->second;
     bool resource_lost = true;
-    UIResourceRequest request(UIResourceRequest::UIResourceCreate,
-                              uid,
+    UIResourceRequest request(UIResourceRequest::UI_RESOURCE_CREATE, uid,
                               data.client->GetBitmap(uid, resource_lost));
     ui_resource_request_queue_.push_back(request);
   }
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index b17b5a4..d41e73d 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -113,6 +113,7 @@
   void WillBeginMainFrame();
   void DidBeginMainFrame();
   void BeginMainFrame(const BeginFrameArgs& args);
+  void BeginMainFrameNotExpectedSoon();
   void AnimateLayers(base::TimeTicks monotonic_frame_begin_time);
   void DidStopFlinging();
   void Layout();
@@ -447,20 +448,6 @@
 
   bool in_paint_layer_contents_;
 
-  static const int kTotalFramesToUseForLCDTextMetrics = 50;
-  int total_frames_used_for_lcd_text_metrics_;
-
-  struct LCDTextMetrics {
-    LCDTextMetrics()
-        : total_num_cc_layers(0),
-          total_num_cc_layers_can_use_lcd_text(0),
-          total_num_cc_layers_will_use_lcd_text(0) {}
-
-    int64 total_num_cc_layers;
-    int64 total_num_cc_layers_can_use_lcd_text;
-    int64 total_num_cc_layers_will_use_lcd_text;
-  };
-  LCDTextMetrics lcd_text_metrics_;
   int id_;
   bool next_commit_forces_redraw_;
 
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h
index 574325b..6b30532 100644
--- a/cc/trees/layer_tree_host_client.h
+++ b/cc/trees/layer_tree_host_client.h
@@ -26,6 +26,7 @@
   // Marks finishing compositing-related tasks on the main thread. In threaded
   // mode, this corresponds to DidCommit().
   virtual void BeginMainFrame(const BeginFrameArgs& args) = 0;
+  virtual void BeginMainFrameNotExpectedSoon() = 0;
   virtual void DidBeginMainFrame() = 0;
   virtual void Layout() = 0;
   virtual void ApplyViewportDeltas(
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 70c5037..7a3b5ad 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -161,8 +161,8 @@
 }
 
 enum TranslateRectDirection {
-  TranslateRectDirectionToAncestor,
-  TranslateRectDirectionToDescendant
+  TRANSLATE_RECT_DIRECTION_TO_ANCESTOR,
+  TRANSLATE_RECT_DIRECTION_TO_DESCENDANT
 };
 
 template <typename LayerType>
@@ -172,7 +172,7 @@
                                             TranslateRectDirection direction) {
   gfx::Vector2dF translation = ComputeChangeOfBasisTranslation<LayerType>(
       ancestor_layer, descendant_layer);
-  if (direction == TranslateRectDirectionToDescendant)
+  if (direction == TRANSLATE_RECT_DIRECTION_TO_DESCENDANT)
     translation.Scale(-1.f);
   return gfx::ToEnclosingRect(
       gfx::RectF(rect.origin() + translation, rect.size()));
@@ -211,20 +211,16 @@
   // clip rects will want to be in its target space, not ours.
   if (clip_parent == layer->clip_parent()) {
     *clip_rect_in_parent_target_space = TranslateRectToTargetSpace<LayerType>(
-        *clip_parent,
-        *layer->parent(),
-        *clip_rect_in_parent_target_space,
-        TranslateRectDirectionToDescendant);
+        *clip_parent, *layer->parent(), *clip_rect_in_parent_target_space,
+        TRANSLATE_RECT_DIRECTION_TO_DESCENDANT);
   } else {
     // If we're being clipped by our scroll parent, we must translate through
     // our common ancestor. This happens to be our parent, so it is sufficent to
     // translate from our clip parent's space to the space of its ancestor (our
     // parent).
-    *clip_rect_in_parent_target_space =
-        TranslateRectToTargetSpace<LayerType>(*layer->parent(),
-                                              *clip_parent,
-                                              *clip_rect_in_parent_target_space,
-                                              TranslateRectDirectionToAncestor);
+    *clip_rect_in_parent_target_space = TranslateRectToTargetSpace<LayerType>(
+        *layer->parent(), *clip_parent, *clip_rect_in_parent_target_space,
+        TRANSLATE_RECT_DIRECTION_TO_ANCESTOR);
   }
 }
 
@@ -286,10 +282,8 @@
     // so we'll need to transform it before it is applied.
     if (layer->clip_parent()) {
       clip_rect = TranslateRectToTargetSpace<LayerType>(
-          *layer->clip_parent(),
-          *layer,
-          clip_rect,
-          TranslateRectDirectionToDescendant);
+          *layer->clip_parent(), *layer, clip_rect,
+          TRANSLATE_RECT_DIRECTION_TO_DESCENDANT);
     }
     target_rect.Intersect(clip_rect);
   }
@@ -1203,12 +1197,30 @@
   }
 };
 
+static bool ValidateRenderSurface(LayerImpl* layer) {
+  // This test verifies that there are no cases where a LayerImpl needs
+  // a render surface, but doesn't have one.
+  if (layer->render_surface())
+    return true;
+
+  return layer->filters().IsEmpty() && layer->background_filters().IsEmpty() &&
+         !layer->mask_layer() && !layer->replica_layer() &&
+         !IsRootLayer(layer) && !layer->is_root_for_isolated_group() &&
+         !layer->HasCopyRequest();
+}
+
+static bool ValidateRenderSurface(Layer* layer) {
+  return true;
+}
+
 // Recursively walks the layer tree to compute any information that is needed
 // before doing the main recursion.
 template <typename LayerType>
 static void PreCalculateMetaInformation(
     LayerType* layer,
     PreCalculateMetaInformationRecursiveData* recursive_data) {
+  DCHECK(ValidateRenderSurface(layer));
+
   layer->draw_properties().sorted_for_recursion = false;
   layer->draw_properties().has_child_with_a_scroll_parent = false;
 
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index de5f44a..30cc15a 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -5677,56 +5677,60 @@
 class LCDTextTest
     : public LayerTreeHostCommonTestBase,
       public testing::TestWithParam<LCDTextTestParam> {
+ public:
+  LCDTextTest()
+      : host_impl_(&proxy_, &shared_bitmap_manager_),
+        root_(nullptr),
+        child_(nullptr),
+        grand_child_(nullptr) {}
+
  protected:
   void SetUp() override {
     can_use_lcd_text_ = std::tr1::get<0>(GetParam());
     layers_always_allowed_lcd_text_ = std::tr1::get<1>(GetParam());
 
-    root_ = Layer::Create();
-    child_ = Layer::Create();
-    grand_child_ = Layer::Create();
-    child_->AddChild(grand_child_.get());
-    root_->AddChild(child_.get());
+    scoped_ptr<LayerImpl> root_ptr =
+        LayerImpl::Create(host_impl_.active_tree(), 1);
+    scoped_ptr<LayerImpl> child_ptr =
+        LayerImpl::Create(host_impl_.active_tree(), 2);
+    scoped_ptr<LayerImpl> grand_child_ptr =
+        LayerImpl::Create(host_impl_.active_tree(), 3);
+
+    // Stash raw pointers to look at later.
+    root_ = root_ptr.get();
+    child_ = child_ptr.get();
+    grand_child_ = grand_child_ptr.get();
+
+    child_->AddChild(grand_child_ptr.Pass());
+    root_->AddChild(child_ptr.Pass());
+    host_impl_.active_tree()->SetRootLayer(root_ptr.Pass());
 
     root_->SetContentsOpaque(true);
     child_->SetContentsOpaque(true);
     grand_child_->SetContentsOpaque(true);
 
     gfx::Transform identity_matrix;
-    SetLayerPropertiesForTesting(root_.get(),
-                                 identity_matrix,
-                                 gfx::Point3F(),
-                                 gfx::PointF(),
-                                 gfx::Size(1, 1),
-                                 true,
+    SetLayerPropertiesForTesting(root_, identity_matrix, gfx::Point3F(),
+                                 gfx::PointF(), gfx::Size(1, 1), true, false,
+                                 true);
+    SetLayerPropertiesForTesting(child_, identity_matrix, gfx::Point3F(),
+                                 gfx::PointF(), gfx::Size(1, 1), true, false,
+                                 std::tr1::get<2>(GetParam()));
+    SetLayerPropertiesForTesting(grand_child_, identity_matrix, gfx::Point3F(),
+                                 gfx::PointF(), gfx::Size(1, 1), true, false,
                                  false);
-    SetLayerPropertiesForTesting(child_.get(),
-                                 identity_matrix,
-                                 gfx::Point3F(),
-                                 gfx::PointF(),
-                                 gfx::Size(1, 1),
-                                 true,
-                                 false);
-    SetLayerPropertiesForTesting(grand_child_.get(),
-                                 identity_matrix,
-                                 gfx::Point3F(),
-                                 gfx::PointF(),
-                                 gfx::Size(1, 1),
-                                 true,
-                                 false);
-
-    child_->SetForceRenderSurface(std::tr1::get<2>(GetParam()));
-
-    host_ = CreateFakeLayerTreeHost();
-    host_->SetRootLayer(root_);
   }
 
   bool can_use_lcd_text_;
   bool layers_always_allowed_lcd_text_;
-  scoped_ptr<FakeLayerTreeHost> host_;
-  scoped_refptr<Layer> root_;
-  scoped_refptr<Layer> child_;
-  scoped_refptr<Layer> grand_child_;
+
+  FakeImplProxy proxy_;
+  TestSharedBitmapManager shared_bitmap_manager_;
+  FakeLayerTreeHostImpl host_impl_;
+
+  LayerImpl* root_;
+  LayerImpl* child_;
+  LayerImpl* grand_child_;
 };
 
 TEST_P(LCDTextTest, CanUseLCDText) {
@@ -5735,7 +5739,7 @@
 
   // Case 1: Identity transform.
   gfx::Transform identity_matrix;
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5745,7 +5749,7 @@
   gfx::Transform integral_translation;
   integral_translation.Translate(1.0, 2.0);
   child_->SetTransform(integral_translation);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5755,7 +5759,7 @@
   gfx::Transform non_integral_translation;
   non_integral_translation.Translate(1.5, 2.5);
   child_->SetTransform(non_integral_translation);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5765,7 +5769,7 @@
   gfx::Transform rotation;
   rotation.Rotate(10.0);
   child_->SetTransform(rotation);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5775,7 +5779,7 @@
   gfx::Transform scale;
   scale.Scale(2.0, 2.0);
   child_->SetTransform(scale);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5785,7 +5789,7 @@
   gfx::Transform skew;
   skew.SkewX(10.0);
   child_->SetTransform(skew);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5794,7 +5798,7 @@
   // Case 7: Translucent.
   child_->SetTransform(identity_matrix);
   child_->SetOpacity(0.5f);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5803,7 +5807,7 @@
   // Case 8: Sanity check: restore transform and opacity.
   child_->SetTransform(identity_matrix);
   child_->SetOpacity(1.f);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5811,7 +5815,7 @@
 
   // Case 9: Non-opaque content.
   child_->SetContentsOpaque(false);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5819,7 +5823,7 @@
 
   // Case 10: Sanity check: restore content opaqueness.
   child_->SetContentsOpaque(true);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5830,7 +5834,7 @@
   bool expect_lcd_text = can_use_lcd_text_ || layers_always_allowed_lcd_text_;
 
   // Sanity check: Make sure can_use_lcd_text_ is set on each node.
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5841,7 +5845,7 @@
   AddOpacityTransitionToController(
       child_->layer_animation_controller(), 10.0, 0.9f, 0.1f, false);
 
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   // Text AA should not be adjusted while animation is active.
   // Make sure LCD text AA setting remains unchanged.
@@ -7971,11 +7975,11 @@
       0.f, grand_child_raw->draw_properties().maximum_animation_contents_scale);
 
   grand_parent->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
   parent_raw->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
   child_raw->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
 
   TransformOperations perspective;
   perspective.AppendPerspective(10.f);
@@ -7995,7 +7999,7 @@
       0.f, grand_child_raw->draw_properties().maximum_animation_contents_scale);
 
   child_raw->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
 
   gfx::Transform scale_matrix;
   scale_matrix.Scale(1.f, 2.f);
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 3cb2858..eef1cc3 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -307,8 +307,11 @@
   if (settings_.impl_side_painting) {
     // Impl-side painting needs an update immediately post-commit to have the
     // opportunity to create tilings.  Other paths can call UpdateDrawProperties
-    // more lazily when needed prior to drawing.
-    sync_tree()->UpdateDrawProperties();
+    // more lazily when needed prior to drawing.  Because invalidations may
+    // be coming from the main thread, it's safe to do an update for lcd text
+    // at this point and see if lcd text needs to be disabled on any layers.
+    bool update_lcd_text = true;
+    sync_tree()->UpdateDrawProperties(update_lcd_text);
     // Start working on newly created tiles immediately if needed.
     if (tile_manager_ && tile_priorities_dirty_)
       PrepareTiles();
@@ -473,7 +476,7 @@
 }
 
 static ScrollBlocksOn EffectiveScrollBlocksOn(LayerImpl* layer) {
-  ScrollBlocksOn blocks = ScrollBlocksOnNone;
+  ScrollBlocksOn blocks = SCROLL_BLOCKS_ON_NONE;
   for (; layer; layer = NextScrollLayer(layer)) {
     blocks |= layer->scroll_blocks_on();
   }
@@ -492,7 +495,7 @@
   LayerImpl* layer_impl =
       active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
   ScrollBlocksOn blocking = EffectiveScrollBlocksOn(layer_impl);
-  if (!(blocking & ScrollBlocksOnStartTouch))
+  if (!(blocking & SCROLL_BLOCKS_ON_START_TOUCH))
     return false;
 
   // Now determine if there are actually any handlers at that point.
@@ -1046,7 +1049,8 @@
   UMA_HISTOGRAM_CUSTOM_COUNTS(
       "Compositing.NumActiveLayers", active_tree_->NumLayers(), 1, 400, 20);
 
-  bool ok = active_tree_->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  bool ok = active_tree_->UpdateDrawProperties(update_lcd_text);
   DCHECK(ok) << "UpdateDrawProperties failed during draw";
 
   // This will cause NotifyTileStateChanged() to be called for any visible tiles
@@ -1619,12 +1623,19 @@
   }
   CompositorFrameMetadata metadata = MakeCompositorFrameMetadata();
   active_tree()->FinishSwapPromises(&metadata);
-  for (size_t i = 0; i < metadata.latency_info.size(); i++) {
+  for (auto& latency : metadata.latency_info) {
     TRACE_EVENT_FLOW_STEP0(
         "input,benchmark",
         "LatencyInfo.Flow",
-        TRACE_ID_DONT_MANGLE(metadata.latency_info[i].trace_id),
+        TRACE_ID_DONT_MANGLE(latency.trace_id),
         "SwapBuffers");
+    // Only add the latency component once for renderer swap, not the browser
+    // swap.
+    if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
+                             0, nullptr)) {
+      latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
+                               0, 0);
+    }
   }
   renderer_->SwapBuffers(metadata);
   return true;
@@ -2295,7 +2306,7 @@
     // thread.
     ScrollStatus status =
         layer_impl->TryScroll(device_viewport_point, type, block_mode);
-    if (status == ScrollOnMainThread) {
+    if (status == SCROLL_ON_MAIN_THREAD) {
       *scroll_on_main_thread = true;
       return NULL;
     }
@@ -2307,7 +2318,7 @@
     status =
         scroll_layer_impl->TryScroll(device_viewport_point, type, block_mode);
     // If any layer wants to divert the scroll event to the main thread, abort.
-    if (status == ScrollOnMainThread) {
+    if (status == SCROLL_ON_MAIN_THREAD) {
       *scroll_on_main_thread = true;
       return NULL;
     }
@@ -2316,7 +2327,7 @@
         scroll_layer_impl->have_scroll_event_handlers())
       *optional_has_ancestor_scroll_handler = true;
 
-    if (status == ScrollStarted && !potentially_scrolling_layer_impl)
+    if (status == SCROLL_STARTED && !potentially_scrolling_layer_impl)
       potentially_scrolling_layer_impl = scroll_layer_impl;
   }
 
@@ -2363,7 +2374,7 @@
         active_tree_->FindFirstScrollingLayerThatIsHitByPoint(
             device_viewport_point);
     if (scroll_layer_impl && !HasScrollAncestor(layer_impl, scroll_layer_impl))
-      return ScrollUnknown;
+      return SCROLL_UNKNOWN;
   }
 
   bool scroll_on_main_thread = false;
@@ -2376,18 +2387,18 @@
 
   if (scroll_on_main_thread) {
     UMA_HISTOGRAM_BOOLEAN("TryScroll.SlowScroll", true);
-    return ScrollOnMainThread;
+    return SCROLL_ON_MAIN_THREAD;
   }
 
   if (scrolling_layer_impl) {
     active_tree_->SetCurrentlyScrollingLayer(scrolling_layer_impl);
-    should_bubble_scrolls_ = (type != NonBubblingGesture);
-    wheel_scrolling_ = (type == Wheel);
+    should_bubble_scrolls_ = (type != NON_BUBBLING_GESTURE);
+    wheel_scrolling_ = (type == WHEEL);
     client_->RenewTreePriority();
     UMA_HISTOGRAM_BOOLEAN("TryScroll.SlowScroll", false);
-    return ScrollStarted;
+    return SCROLL_STARTED;
   }
-  return ScrollIgnored;
+  return SCROLL_IGNORED;
 }
 
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated(
@@ -2396,9 +2407,9 @@
   if (LayerImpl* layer_impl = CurrentlyScrollingLayer()) {
     Animation* animation =
         layer_impl->layer_animation_controller()->GetAnimation(
-            Animation::ScrollOffset);
+            Animation::SCROLL_OFFSET);
     if (!animation)
-      return ScrollIgnored;
+      return SCROLL_IGNORED;
 
     ScrollOffsetAnimationCurve* curve =
         animation->curve()->ToScrollOffsetAnimationCurve();
@@ -2413,13 +2424,13 @@
                        CurrentBeginFrameArgs().frame_time).InSecondsF(),
         new_target);
 
-    return ScrollStarted;
+    return SCROLL_STARTED;
   }
   // ScrollAnimated is only used for wheel scrolls. We use the same bubbling
   // behavior as ScrollBy to determine which layer to animate, but we do not
   // do the Android-specific things in ScrollBy like showing top controls.
-  InputHandler::ScrollStatus scroll_status = ScrollBegin(viewport_point, Wheel);
-  if (scroll_status == ScrollStarted) {
+  InputHandler::ScrollStatus scroll_status = ScrollBegin(viewport_point, WHEEL);
+  if (scroll_status == SCROLL_STARTED) {
     gfx::Vector2dF pending_delta = scroll_delta;
     for (LayerImpl* layer_impl = CurrentlyScrollingLayer(); layer_impl;
          layer_impl = layer_impl->parent()) {
@@ -2450,17 +2461,15 @@
                                              EaseInOutTimingFunction::Create());
       curve->SetInitialValue(current_offset);
 
-      scoped_ptr<Animation> animation =
-          Animation::Create(curve.Pass(),
-                            AnimationIdProvider::NextAnimationId(),
-                            AnimationIdProvider::NextGroupId(),
-                            Animation::ScrollOffset);
+      scoped_ptr<Animation> animation = Animation::Create(
+          curve.Pass(), AnimationIdProvider::NextAnimationId(),
+          AnimationIdProvider::NextGroupId(), Animation::SCROLL_OFFSET);
       animation->set_is_impl_only(true);
 
       layer_impl->layer_animation_controller()->AddAnimation(animation.Pass());
 
       SetNeedsAnimate();
-      return ScrollStarted;
+      return SCROLL_STARTED;
     }
   }
   ScrollEnd();
@@ -2811,13 +2820,13 @@
 
 InputHandler::ScrollStatus LayerTreeHostImpl::FlingScrollBegin() {
   if (!active_tree_->CurrentlyScrollingLayer())
-    return ScrollIgnored;
+    return SCROLL_IGNORED;
 
   if (settings_.ignore_root_layer_flings &&
       (active_tree_->CurrentlyScrollingLayer() == InnerViewportScrollLayer() ||
        active_tree_->CurrentlyScrollingLayer() == OuterViewportScrollLayer())) {
     ClearCurrentlyScrollingLayer();
-    return ScrollIgnored;
+    return SCROLL_IGNORED;
   }
 
   if (!wheel_scrolling_) {
@@ -2827,7 +2836,7 @@
     should_bubble_scrolls_ = false;
   }
 
-  return ScrollStarted;
+  return SCROLL_STARTED;
 }
 
 float LayerTreeHostImpl::DeviceSpaceDistanceToLayer(
@@ -2871,12 +2880,9 @@
   }
 
   bool scroll_on_main_thread = false;
-  LayerImpl* scroll_layer_impl =
-      FindScrollLayerForDeviceViewportPoint(device_viewport_point,
-                                            InputHandler::Gesture,
-                                            layer_impl,
-                                            &scroll_on_main_thread,
-                                            NULL);
+  LayerImpl* scroll_layer_impl = FindScrollLayerForDeviceViewportPoint(
+      device_viewport_point, InputHandler::GESTURE, layer_impl,
+      &scroll_on_main_thread, NULL);
   if (scroll_on_main_thread || !scroll_layer_impl)
     return;
 
@@ -3373,11 +3379,9 @@
       format = ETC1;
       break;
   }
-  id =
-      resource_provider_->CreateResource(bitmap.GetSize(),
-                                         wrap_mode,
-                                         ResourceProvider::TextureHintImmutable,
-                                         format);
+  id = resource_provider_->CreateResource(
+      bitmap.GetSize(), wrap_mode, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      format);
 
   UIResourceData data;
   data.resource_id = id;
@@ -3387,11 +3391,8 @@
   ui_resource_map_[uid] = data;
 
   AutoLockUIResourceBitmap bitmap_lock(bitmap);
-  resource_provider_->SetPixels(id,
-                                bitmap_lock.GetPixels(),
-                                gfx::Rect(bitmap.GetSize()),
-                                gfx::Rect(bitmap.GetSize()),
-                                gfx::Vector2d(0, 0));
+  resource_provider_->CopyToResource(id, bitmap_lock.GetPixels(),
+                                     bitmap.GetSize());
   MarkUIResourceNotEvicted(uid);
 }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 6dd0d5a..60f4b44 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -328,6 +328,8 @@
   const LayerTreeImpl* recycle_tree() const { return recycle_tree_.get(); }
   // Returns the tree LTH synchronizes with.
   LayerTreeImpl* sync_tree() {
+    // TODO(enne): This is bogus.  It should return based on the value of
+    // Proxy::CommitToActiveTree and not whether the pending tree exists.
     return pending_tree_ ? pending_tree_.get() : active_tree_.get();
   }
   virtual void CreatePendingTree();
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 07fb048..38b2d66 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -497,16 +497,16 @@
   host_impl_->SetViewportSize(gfx::Size(50, 50));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
-                                                      InputHandler::Wheel));
+                                                      InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10),
-                                                      InputHandler::Wheel));
+                                                      InputHandler::WHEEL));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
-                                                       InputHandler::Wheel));
+                                                       InputHandler::WHEEL));
   EXPECT_TRUE(did_request_redraw_);
   EXPECT_TRUE(did_request_commit_);
 }
@@ -516,8 +516,8 @@
   host_impl_->SetViewportSize(gfx::Size(50, 50));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   EXPECT_FALSE(host_impl_->IsActivelyScrolling());
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_TRUE(host_impl_->IsActivelyScrolling());
@@ -527,8 +527,8 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
   // We should not crash when trying to scroll an empty layer tree.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
@@ -544,8 +544,8 @@
 
   // We should not crash when trying to scroll after the renderer initialization
   // fails.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
@@ -554,8 +554,8 @@
   DrawFrame();
 
   // We should not crash if the tree is replaced while we are scrolling.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->active_tree()->DetachLayerTree();
 
   scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
@@ -578,25 +578,26 @@
   // With registered event handlers, wheel scrolls don't necessarily
   // have to go to the main thread.
   root->SetHaveWheelEventHandlers(true);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
 
   // But typically the scroll-blocks-on mode will require them to.
-  root->SetScrollBlocksOn(ScrollBlocksOnWheelEvent | ScrollBlocksOnStartTouch);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT |
+                          SCROLL_BLOCKS_ON_START_TOUCH);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // But gesture scrolls can still be handled.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // And if the handlers go away, wheel scrolls can again be processed
   // on impl (despite the scroll-blocks-on mode).
   root->SetHaveWheelEventHandlers(false);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
 }
 
@@ -621,20 +622,21 @@
   // Touch handler regions determine whether touch events block scroll.
   root->SetTouchEventHandlerRegion(gfx::Rect(0, 0, 100, 100));
   EXPECT_FALSE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10)));
-  root->SetScrollBlocksOn(ScrollBlocksOnStartTouch | ScrollBlocksOnWheelEvent);
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_START_TOUCH |
+                          SCROLL_BLOCKS_ON_WHEEL_EVENT);
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10)));
 
   // But they don't influence the actual handling of the scroll gestures.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // It's the union of scroll-blocks-on mode bits across all layers in the
   // scroll paret chain that matters.
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
-  root->SetScrollBlocksOn(ScrollBlocksOnNone);
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_NONE);
   EXPECT_FALSE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
-  child->SetScrollBlocksOn(ScrollBlocksOnStartTouch);
+  child->SetScrollBlocksOn(SCROLL_BLOCKS_ON_START_TOUCH);
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
 }
 
@@ -647,30 +649,31 @@
   // With registered scroll handlers, scrolls don't generally have to go
   // to the main thread.
   root->SetHaveScrollEventHandlers(true);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
 
   // Even the default scroll blocks on mode doesn't require this.
-  root->SetScrollBlocksOn(ScrollBlocksOnWheelEvent | ScrollBlocksOnStartTouch);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT |
+                          SCROLL_BLOCKS_ON_START_TOUCH);
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // But the page can opt in to blocking on scroll event handlers.
-  root->SetScrollBlocksOn(ScrollBlocksOnScrollEvent);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
-  // Gesture and Wheel scrolls behave identically in this regard.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  // GESTURE and WHEEL scrolls behave identically in this regard.
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // And if the handlers go away, scrolls can again be processed on impl
   // (despite the scroll-blocks-on mode).
   root->SetHaveScrollEventHandlers(false);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 }
 
@@ -713,36 +716,36 @@
   }
 
   // Scroll-blocks-on on a layer affects scrolls that hit that layer.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
-  child1->SetScrollBlocksOn(ScrollBlocksOnScrollEvent);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture));
+  child1->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE));
 
   // But not those that hit only other layers.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // It's the union of bits set across the scroll ancestor chain that matters.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
-  root->SetScrollBlocksOn(ScrollBlocksOnWheelEvent);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Wheel));
-  child2->SetScrollBlocksOn(ScrollBlocksOnScrollEvent);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Wheel));
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::WHEEL));
+  child2->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::WHEEL));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) {
@@ -751,16 +754,14 @@
   DrawFrame();
 
   // Ignore the fling since no layer is being scrolled
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Now the fling should go ahead since we've started scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 }
 
 TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) {
@@ -769,16 +770,14 @@
   DrawFrame();
 
   // Ignore the fling since no layer is being scrolled
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // Now the fling should go ahead since we've started scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 }
 
 TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) {
@@ -790,12 +789,11 @@
   root->SetShouldScrollOnMainThread(true);
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // The fling should be ignored since there's no layer being scrolled impl-side
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
 }
 
 TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
@@ -806,10 +804,10 @@
 
   root->SetShouldScrollOnMainThread(true);
 
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
@@ -823,38 +821,34 @@
   DrawFrame();
 
   // All scroll types inside the non-fast scrollable region should fail.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(25, 25),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(25, 25), InputHandler::WHEEL));
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
-                                                       InputHandler::Wheel));
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(25, 25),
-                                    InputHandler::Gesture));
+                                                       InputHandler::WHEEL));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(25, 25), InputHandler::GESTURE));
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
-                                                       InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
 
   // All scroll types outside this region should succeed.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(75, 75),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(75, 75), InputHandler::WHEEL));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                      InputHandler::Gesture));
+                                                      InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
-                                                       InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                       InputHandler::Gesture));
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(75, 75),
-                                    InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(75, 75), InputHandler::GESTURE));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                      InputHandler::Gesture));
+                                                      InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                       InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
@@ -870,18 +864,16 @@
 
   // This point would fall into the non-fast scrollable region except that we've
   // moved the layer down by 25 pixels.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(40, 10),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(40, 10), InputHandler::WHEEL));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10),
-                                                      InputHandler::Wheel));
+                                                      InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 1));
   host_impl_->ScrollEnd();
 
   // This point is still inside the non-fast region.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 10),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
@@ -891,7 +883,7 @@
   DrawFrame();
 
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
@@ -904,7 +896,7 @@
   DrawFrame();
 
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   EXPECT_TRUE(host_impl_->scroll_affects_scroll_handler());
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
@@ -916,8 +908,8 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Trying to scroll to the left/top will not succeed.
   EXPECT_FALSE(
@@ -962,9 +954,8 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // Trying to scroll without a vertical scrollbar will fail.
   EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
@@ -1006,8 +997,8 @@
   DrawFrame();
   gfx::Point scroll_position(10, 10);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(scroll_position, InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(scroll_position, InputHandler::WHEEL));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset());
 
@@ -1019,8 +1010,8 @@
 
   overflow->set_user_scrollable_horizontal(false);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(scroll_position, InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(scroll_position, InputHandler::WHEEL));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset());
 
@@ -1031,8 +1022,8 @@
 
   overflow->set_user_scrollable_vertical(false);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(scroll_position, InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(scroll_position, InputHandler::WHEEL));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 0), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
 
@@ -1063,7 +1054,7 @@
 
     float page_scale_delta = 2.f;
 
-    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
     host_impl_->PinchGestureEnd();
@@ -1090,16 +1081,15 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
 
     float page_scale_delta = 2.f;
-    host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
     host_impl_->PinchGestureEnd();
     host_impl_->ScrollEnd();
 
     gfx::Vector2d scroll_delta(0, 10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -1117,8 +1107,8 @@
       new LatencyInfoSwapPromise(latency_info));
 
   SetupScrollAndContentsLayers(gfx::Size(100, 100));
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(swap_promise.Pass());
   host_impl_->ScrollEnd();
@@ -1146,7 +1136,7 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
 
     float page_scale_delta = 2.f;
-    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
     host_impl_->PinchGestureEnd();
@@ -1167,7 +1157,7 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
     float page_scale_delta = 10.f;
 
-    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
     host_impl_->PinchGestureEnd();
@@ -1187,7 +1177,7 @@
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(50, 50));
 
     float page_scale_delta = 0.1f;
-    host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
     host_impl_->PinchGestureEnd();
@@ -1209,7 +1199,7 @@
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(20, 20));
 
     float page_scale_delta = 1.f;
-    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
@@ -1231,7 +1221,7 @@
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(20, 20));
 
     float page_scale_delta = 1.f;
-    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
     host_impl_->ScrollBy(gfx::Point(10, 10), gfx::Vector2d(-10, -10));
@@ -1252,7 +1242,7 @@
     scroll_layer->PullDeltaForMainThread();
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(0, 0));
 
-    host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(2.f, gfx::Point(0, 0));
     host_impl_->PinchGestureUpdate(1.f, gfx::Point(0, 0));
@@ -1618,7 +1608,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
   LayerTreeSettings settings;
-  settings.scrollbar_animator = LayerTreeSettings::LinearFade;
+  settings.scrollbar_animator = LayerTreeSettings::LINEAR_FADE;
   settings.scrollbar_fade_delay_ms = 20;
   settings.scrollbar_fade_duration_ms = 20;
 
@@ -1630,14 +1620,14 @@
   EXPECT_FALSE(did_request_redraw_);
 
   // If no scroll happened during a scroll gesture, it should have no effect.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollEnd();
   EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_TRUE(scrollbar_fade_start_.Equals(base::Closure()));
 
   // After a scroll, a fade animation should be scheduled about 20ms from now.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 5));
   host_impl_->ScrollEnd();
   did_request_redraw_ = false;
@@ -1668,7 +1658,7 @@
   requested_scrollbar_animation_delay_ = base::TimeDelta();
 
   // Unnecessarily Fade animation of solid color scrollbar is not triggered.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
   host_impl_->ScrollEnd();
   EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
@@ -1676,7 +1666,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollbarFadePinchZoomScrollbars) {
   LayerTreeSettings settings;
-  settings.scrollbar_animator = LayerTreeSettings::LinearFade;
+  settings.scrollbar_animator = LayerTreeSettings::LINEAR_FADE;
   settings.scrollbar_fade_delay_ms = 20;
   settings.scrollbar_fade_duration_ms = 20;
   settings.use_pinch_zoom_scrollbars = true;
@@ -1691,14 +1681,14 @@
   EXPECT_FALSE(did_request_animate_);
 
   // If no scroll happened during a scroll gesture, it should have no effect.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollEnd();
   EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
   EXPECT_FALSE(did_request_animate_);
   EXPECT_TRUE(scrollbar_fade_start_.Equals(base::Closure()));
 
   // After a scroll, no fade animation should be scheduled.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
   host_impl_->ScrollEnd();
   did_request_redraw_ = false;
@@ -1715,7 +1705,7 @@
   host_impl_->SetPageScaleOnActiveTree(1.1f);
 
   // After a scroll, a fade animation should be scheduled about 20ms from now.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
   host_impl_->ScrollEnd();
   did_request_redraw_ = false;
@@ -1737,7 +1727,7 @@
   LayerTreeSettings settings;
   settings.scrollbar_fade_delay_ms = 500;
   settings.scrollbar_fade_duration_ms = 300;
-  settings.scrollbar_animator = LayerTreeSettings::Thinning;
+  settings.scrollbar_animator = LayerTreeSettings::THINNING;
 
   gfx::Size viewport_size(300, 200);
   gfx::Size device_viewport_size = gfx::ToFlooredSize(
@@ -1844,8 +1834,8 @@
   }
 
   // Scrolling should update metadata immediately.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   {
     CompositorFrameMetadata metadata =
@@ -1878,7 +1868,7 @@
   }
 
   // Page scale should update metadata correctly (shrinking only the viewport).
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(2.f, gfx::Point());
   host_impl_->PinchGestureEnd();
@@ -2444,8 +2434,8 @@
   DrawFrame();
 
   // Scroll event is ignored because layer is not scrollable.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -2602,8 +2592,8 @@
       gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Make the test scroll delta a fractional amount, to verify that the
   // fixed container size delta is (1) non-zero, and (2) fractional, and
@@ -2643,8 +2633,8 @@
   outer_scroll->SetDrawsContent(true);
   host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0.f, 50.f));
 
   // The entire scroll delta should have been used to hide the top controls.
@@ -2662,8 +2652,8 @@
 
   host_impl_->ScrollEnd();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), inner_scroll);
 
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0.f, -50.f));
@@ -2709,8 +2699,8 @@
   // not be scaled.
   host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1.f, 2.f);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Scroll down, the top controls hiding should expand the viewport size so
   // the delta should be equal to the scroll distance.
@@ -2781,8 +2771,8 @@
 
   // Scroll 25px to hide top controls
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -2930,8 +2920,8 @@
 
   // Hide the top controls by 25px.
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 
   // scrolling down at the max extents no longer hides the top controls
@@ -2956,8 +2946,8 @@
 
   // Bring the top controls down by 25px.
   scroll_delta = gfx::Vector2dF(0.f, -25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -2983,8 +2973,8 @@
                   host_impl_->top_controls_manager()->ContentTopOffset());
 
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3021,8 +3011,8 @@
   // Send a gesture scroll that will scroll the outer viewport, make sure the
   // top controls get scrolled.
   gfx::Vector2dF scroll_delta(0.f, 15.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   EXPECT_EQ(host_impl_->OuterViewportScrollLayer(),
             host_impl_->CurrentlyScrollingLayer());
@@ -3033,8 +3023,8 @@
                       host_impl_->top_controls_manager()->ContentTopOffset());
 
   scroll_delta = gfx::Vector2dF(0.f, 50.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ContentTopOffset());
@@ -3049,8 +3039,8 @@
   host_impl_->InnerViewportScrollLayer()->SetScrollDelta(inner_viewport_offset);
 
   scroll_delta = gfx::Vector2dF(0.f, -65.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 
   EXPECT_EQ(top_controls_height_,
@@ -3068,8 +3058,8 @@
   SetupTopControlsAndScrollLayer();
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   host_impl_->top_controls_manager()->ScrollBegin();
   host_impl_->top_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 50.f));
@@ -3081,8 +3071,8 @@
 
   host_impl_->ScrollEnd();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   float scroll_increment_y = -25.f;
   host_impl_->top_controls_manager()->ScrollBegin();
@@ -3110,8 +3100,8 @@
       gfx::ScrollOffset(),
       host_impl_->active_tree()->InnerViewportScrollLayer()->MaxScrollOffset());
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
@@ -3146,9 +3136,8 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->ScrollEnd();
   EXPECT_TRUE(did_request_redraw_);
@@ -3167,9 +3156,8 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->ScrollEnd();
   EXPECT_TRUE(did_request_redraw_);
@@ -3187,9 +3175,8 @@
 
   // Scroll event is ignored because the input coordinate is outside the layer
   // boundaries.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(15, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(15, 5), InputHandler::WHEEL));
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -3213,9 +3200,8 @@
 
   // Scroll event is ignored because the scrollable layer is not facing the
   // viewer and there is nothing scrollable behind it.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -3243,9 +3229,8 @@
 
   // Scrolling fails because the content layer is asking to be scrolled on the
   // main thread.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
@@ -3278,9 +3263,8 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3330,14 +3314,13 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
   // Set new page scale on impl thread by pinching.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(page_scale, gfx::Point());
   host_impl_->PinchGestureEnd();
@@ -3379,7 +3362,7 @@
   LayerImpl* grand_child = child->children()[0];
 
   // Set new page scale on impl thread by pinching.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(new_page_scale, gfx::Point());
   host_impl_->PinchGestureEnd();
@@ -3442,9 +3425,8 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta(scroll_delta);
   gfx::ScrollOffset expected_max_scroll(child->MaxScrollOffset());
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3494,9 +3476,8 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(-8, -7);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3548,9 +3529,9 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, -10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3568,9 +3549,9 @@
 
     // The next time we scroll we should only scroll the parent.
     scroll_delta = gfx::Vector2d(0, -3);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
@@ -3587,9 +3568,9 @@
     // After scrolling the parent, another scroll on the opposite direction
     // should still scroll the child.
     scroll_delta = gfx::Vector2d(0, 7);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
@@ -3609,9 +3590,9 @@
     host_impl_->SetPageScaleOnActiveTree(2.f);
 
     scroll_delta = gfx::Vector2d(0, -2);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(1, 1),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(grand_child, host_impl_->CurrentlyScrollingLayer());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
@@ -3651,9 +3632,8 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, 4);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3698,9 +3678,8 @@
   host_impl_->active_tree()->DidBecomeActive();
 
   // Scrolling should still work even though we did not draw yet.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
@@ -3717,9 +3696,8 @@
 
   // Scroll to the right in screen coordinates with a gesture.
   gfx::Vector2d gesture_scroll_delta(10, 0);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(),
-                                    InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3731,9 +3709,8 @@
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), wheel_scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3779,9 +3756,8 @@
   {
     // Scroll down in screen coordinates with a gesture.
     gfx::Vector2d gesture_scroll_delta(0, 10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(1, 1),
-                                      InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(1, 1), InputHandler::GESTURE));
     host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3802,9 +3778,8 @@
     // Now reset and scroll the same amount horizontally.
     child_ptr->SetScrollDelta(gfx::Vector2dF());
     gfx::Vector2d gesture_scroll_delta(10, 0);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(1, 1),
-                                      InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(1, 1), InputHandler::GESTURE));
     host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3845,8 +3820,8 @@
 
   // Scroll down in screen coordinates with a gesture.
   gfx::Vector2d scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3860,8 +3835,8 @@
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), wheel_scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -4000,7 +3975,7 @@
   // The pinch gesture doesn't put the delegate into a state where the scroll
   // offset is outside of the scroll range.  (this is verified by DCHECKs in the
   // delegate).
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(2.f, gfx::Point());
   host_impl_->PinchGestureUpdate(.5f, gfx::Point());
@@ -4012,8 +3987,8 @@
   gfx::ScrollOffset current_offset(7.f, 8.f);
 
   scroll_delegate.set_getter_return_value(current_offset);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   EXPECT_EQ(ScrollOffsetWithDelta(current_offset, scroll_delta),
@@ -4094,8 +4069,8 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // In-bounds scrolling does not affect overscroll.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_TRUE(scroll_result.did_scroll);
   EXPECT_FALSE(scroll_result.did_overscroll_root);
@@ -4229,9 +4204,9 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, -10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     host_impl_->ScrollEnd();
@@ -4239,9 +4214,9 @@
     // The next time we scroll we should only scroll the parent, but overscroll
     // should still not reach the root layer.
     scroll_delta = gfx::Vector2d(0, -30);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -4252,9 +4227,9 @@
     // After scrolling the parent, another scroll on the opposite direction
     // should scroll the child.
     scroll_delta = gfx::Vector2d(0, 70);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
@@ -4291,9 +4266,8 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, 8);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -4317,8 +4291,8 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // Even though the layer can't scroll the overscroll still happens.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
 }
@@ -4355,8 +4329,8 @@
     // Horizontal & Vertical GlowEffect should not be applied when
     // content size is less then view port size. For Example Horizontal &
     // vertical GlowEffect should not be applied in about:blank page.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, -1));
     EXPECT_EQ(gfx::Vector2dF().ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4392,8 +4366,8 @@
     // Edge glow effect should be applicable only upon reaching Edges
     // of the content. unnecessary glow effect calls shouldn't be
     // called while scrolling up without reaching the edge of the content.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 100));
     EXPECT_EQ(gfx::Vector2dF().ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4403,10 +4377,10 @@
     host_impl_->ScrollEnd();
     // unusedrootDelta should be subtracted from applied delta so that
     // unwanted glow effect calls are not called.
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(0, 0),
-                                      InputHandler::NonBubblingGesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+                                      InputHandler::NON_BUBBLING_GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 20));
     EXPECT_EQ(gfx::Vector2dF(0.000000f, 17.699997f).ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4417,8 +4391,8 @@
     host_impl_->ScrollEnd();
     // TestCase to check  kEpsilon, which prevents minute values to trigger
     // gloweffect without reaching edge.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f));
     EXPECT_EQ(gfx::Vector2dF().ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4493,7 +4467,7 @@
         resource_id_(resource_provider->CreateResource(
             gfx::Size(1, 1),
             GL_CLAMP_TO_EDGE,
-            ResourceProvider::TextureHintImmutable,
+            ResourceProvider::TEXTURE_HINT_IMMUTABLE,
             RGBA_8888)) {
     resource_provider->AllocateForTesting(resource_id_);
     SetBounds(gfx::Size(10, 10));
@@ -6436,7 +6410,8 @@
 
   host_impl_->ActivateSyncTree();
 
-  host_impl_->active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_->active_tree()->UpdateDrawProperties(update_lcd_text);
   ASSERT_EQ(1u, host_impl_->active_tree()->RenderSurfaceLayerList().size());
 
   LayerTreeHostImpl::FrameData frame;
@@ -6917,12 +6892,10 @@
   host_impl_->active_tree()->DidBecomeActive();
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     gfx::Vector2d scroll_delta(0, 100);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -6970,8 +6943,8 @@
     LayerImpl* grand_child = child->children()[0];
 
     gfx::Vector2d scroll_delta(0, -2);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
 
     // The grand child should have scrolled up to its limit.
@@ -6991,7 +6964,7 @@
 
     // The first |ScrollBy| after the fling should re-lock the scrolling
     // layer to the first layer that scrolled, which is the child.
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
 
@@ -7030,11 +7003,10 @@
   host_impl_->active_tree()->DidBecomeActive();
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     gfx::Vector2d scroll_delta(0, 100);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -7053,7 +7025,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) {
   // If we ray cast a scroller that is not on the first layer's ancestor chain,
-  // we should return ScrollUnknown.
+  // we should return SCROLL_UNKNOWN.
   gfx::Size content_size(100, 100);
   SetupScrollAndContentsLayers(content_size);
 
@@ -7079,14 +7051,14 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollUnknown,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
   // If we ray cast a scroller this is on the first layer's ancestor chain, but
   // is not the first scroller we encounter when walking up from the layer, we
-  // should also return ScrollUnknown.
+  // should also return SCROLL_UNKNOWN.
   gfx::Size content_size(100, 100);
   SetupScrollAndContentsLayers(content_size);
 
@@ -7118,8 +7090,8 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollUnknown,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) {
@@ -7145,10 +7117,10 @@
   // it. The reason for this is that if the scrolling the scroll would not move
   // any layer that is a drawn RSLL member, then we can ignore the hit.
   //
-  // Why ScrollStarted? In this case, it's because we've bubbled out and started
-  // overscrolling the inner viewport.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  // Why SCROLL_STARTED? In this case, it's because we've bubbled out and
+  // started overscrolling the inner viewport.
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   EXPECT_EQ(2, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7202,10 +7174,10 @@
   // it. The reason for this is that if the scrolling the scroll would not move
   // any layer that is a drawn RSLL member, then we can ignore the hit.
   //
-  // Why ScrollStarted? In this case, it's because we've bubbled out and started
-  // overscrolling the inner viewport.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  // Why SCROLL_STARTED? In this case, it's because we've bubbled out and
+  // started overscrolling the inner viewport.
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   EXPECT_EQ(7, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7401,8 +7373,8 @@
     LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
 
     // Scrolling normally should not trigger any forwarding.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(
         host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll);
     host_impl_->ScrollEnd();
@@ -7414,8 +7386,8 @@
     // Scrolling with a scroll handler should defer the swap to the main
     // thread.
     scroll_layer->SetHaveScrollEventHandlers(true);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(
         host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll);
     host_impl_->ScrollEnd();
@@ -7490,8 +7462,8 @@
       BOTH, SHOWN, false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF().ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -7549,8 +7521,8 @@
       BOTH, SHOWN, false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF().ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -7619,8 +7591,8 @@
       gfx::ScrollOffset(0, initial_scroll_offset));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -7680,8 +7652,8 @@
                                                              false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
 
   float offset = 50;
@@ -7813,9 +7785,9 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Make sure the fling goes to the outer viewport first
-    EXPECT_EQ(InputHandler::ScrollStarted,
-        host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     gfx::Vector2d scroll_delta(inner_viewport.width(), inner_viewport.height());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -7827,9 +7799,9 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Fling past the outer viewport boundry, make sure inner viewport scrolls.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-        host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     outer_expected += gfx::Vector2dF(scroll_delta.x(), scroll_delta.y());
@@ -7862,9 +7834,9 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Make sure the scroll goes to the outer viewport first.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     // Scroll near the edge of the outer viewport.
     gfx::Vector2d scroll_delta(inner_viewport.width(), inner_viewport.height());
@@ -7908,8 +7880,8 @@
     scoped_ptr<ScrollAndScaleSet> scroll_info;
 
     gfx::Vector2d scroll_delta(0, inner_viewport.height());
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
 
     // The child should have scrolled up to its limit.
@@ -7920,7 +7892,7 @@
 
     // The first |ScrollBy| after the fling should re-lock the scrolling
     // layer to the first layer that scrolled, the inner viewport scroll layer.
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), inner_scroll);
 
@@ -7996,7 +7968,7 @@
   base::TimeTicks start_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
             host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
@@ -8013,7 +7985,7 @@
   EXPECT_TRUE(y > 1 && y < 49);
 
   // Update target.
-  EXPECT_EQ(InputHandler::ScrollStarted,
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
             host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
 
   host_impl_->Animate(start_time + base::TimeDelta::FromMilliseconds(200));
@@ -8356,15 +8328,15 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
 
     float page_scale_delta = 2.f;
-    host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
     host_impl_->PinchGestureEnd();
     host_impl_->ScrollEnd();
 
     gfx::Vector2dF scroll_delta(0, 5);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
     EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
 
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
diff --git a/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc b/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
deleted file mode 100644
index d32817c..0000000
--- a/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/layers/append_quads_data.h"
-#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_layer.h"
-#include "cc/layers/picture_layer_impl.h"
-#include "cc/quads/draw_quad.h"
-#include "cc/test/layer_tree_pixel_test.h"
-#include "cc/trees/layer_tree_impl.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-#if !defined(OS_ANDROID)
-
-namespace cc {
-namespace {
-
-class LayerTreeHostOnDemandRasterPixelTest : public LayerTreePixelTest {
- public:
-  void InitializeSettings(LayerTreeSettings* settings) override {
-    settings->impl_side_painting = true;
-  }
-
-  void BeginCommitOnThread(LayerTreeHostImpl* impl) override {
-    // Not enough memory available. Enforce on-demand rasterization.
-    impl->SetMemoryPolicy(
-        ManagedMemoryPolicy(1, gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING,
-                            1000));
-  }
-
-  void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override {
-    // Find the PictureLayerImpl ask it to append quads to check their material.
-    // The PictureLayerImpl is assumed to be the first child of the root layer
-    // in the active tree.
-    PictureLayerImpl* picture_layer = static_cast<PictureLayerImpl*>(
-        host_impl->active_tree()->root_layer()->child_at(0));
-
-    scoped_ptr<RenderPass> render_pass = RenderPass::Create();
-
-    AppendQuadsData data;
-    picture_layer->AppendQuads(render_pass.get(), &data);
-
-    for (const auto& quad : render_pass->quad_list)
-      EXPECT_EQ(quad->material, DrawQuad::PICTURE_CONTENT);
-
-    // Triggers pixel readback and ends the test.
-    LayerTreePixelTest::SwapBuffersOnThread(host_impl, result);
-  }
-
-  void RunOnDemandRasterPixelTest();
-};
-
-class BlueYellowLayerClient : public ContentLayerClient {
- public:
-  explicit BlueYellowLayerClient(gfx::Rect layer_rect)
-      : layer_rect_(layer_rect) {}
-
-  bool FillsBoundsCompletely() const override { return false; }
-
-  void PaintContents(SkCanvas* canvas,
-                     const gfx::Rect& clip,
-                     PaintingControlSetting picture_control) override {
-    SkPaint paint;
-    paint.setColor(SK_ColorBLUE);
-    canvas->drawRect(SkRect::MakeWH(layer_rect_.width(),
-                                    layer_rect_.height() / 2),
-                     paint);
-
-    paint.setColor(SK_ColorYELLOW);
-    canvas->drawRect(
-        SkRect::MakeXYWH(0,
-                         layer_rect_.height() / 2,
-                         layer_rect_.width(),
-                         layer_rect_.height() / 2),
-        paint);
-  }
-
-  scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
-      const gfx::Rect& clip,
-      PaintingControlSetting picture_control) override {
-    NOTIMPLEMENTED();
-    return DisplayItemList::Create();
-  }
-
- private:
-  gfx::Rect layer_rect_;
-};
-
-void LayerTreeHostOnDemandRasterPixelTest::RunOnDemandRasterPixelTest() {
-  // Use multiple colors in a single layer to prevent bypassing on-demand
-  // rasterization if a single solid color is detected in picture analysis.
-  gfx::Rect layer_rect(200, 200);
-  BlueYellowLayerClient client(layer_rect);
-  scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client);
-
-  layer->SetIsDrawable(true);
-  layer->SetBounds(layer_rect.size());
-  layer->SetPosition(layer_rect.origin());
-
-  RunPixelTest(PIXEL_TEST_GL,
-               layer,
-               base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")));
-}
-
-TEST_F(LayerTreeHostOnDemandRasterPixelTest, RasterPictureLayer) {
-  RunOnDemandRasterPixelTest();
-}
-
-}  // namespace
-}  // namespace cc
-
-#endif  // OS_ANDROID
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index bb6dc17..f9b690d 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -1320,7 +1320,7 @@
     // Make sure partial texture updates are turned off.
     settings->max_partial_texture_updates = 0;
     // Linear fade animator prevents scrollbars from drawing immediately.
-    settings->scrollbar_animator = LayerTreeSettings::NoAnimator;
+    settings->scrollbar_animator = LayerTreeSettings::NO_ANIMATOR;
   }
 
   void SetupTree() override {
@@ -2323,40 +2323,10 @@
 
 class LayerTreeHostTestLCDChange : public LayerTreeHostTest {
  public:
-  class PaintClient : public FakeContentLayerClient {
-   public:
-    PaintClient() : paint_count_(0) {}
-
-    int paint_count() const { return paint_count_; }
-
-    void PaintContents(SkCanvas* canvas,
-                       const gfx::Rect& clip,
-                       PaintingControlSetting picture_control) override {
-      FakeContentLayerClient::PaintContents(canvas, clip, picture_control);
-      ++paint_count_;
-    }
-
-    scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
-        const gfx::Rect& clip,
-        PaintingControlSetting picture_control) override {
-      NOTIMPLEMENTED();
-      return DisplayItemList::Create();
-    }
-
-    bool FillsBoundsCompletely() const override { return false; }
-
-   private:
-    int paint_count_;
-  };
-
   void SetupTree() override {
     num_tiles_rastered_ = 0;
 
-    scoped_refptr<Layer> root_layer;
-    if (layer_tree_host()->settings().impl_side_painting)
-      root_layer = PictureLayer::Create(&client_);
-    else
-      root_layer = ContentLayer::Create(&client_);
+    scoped_refptr<Layer> root_layer = PictureLayer::Create(&client_);
     client_.set_fill_with_nonsolid_color(true);
     root_layer->SetIsDrawable(true);
     root_layer->SetBounds(gfx::Size(10, 10));
@@ -2364,10 +2334,9 @@
 
     layer_tree_host()->SetRootLayer(root_layer);
 
-    // The expecations are based on the assumption that the default
+    // The expectations are based on the assumption that the default
     // LCD settings are:
     EXPECT_TRUE(layer_tree_host()->settings().can_use_lcd_text);
-    EXPECT_FALSE(root_layer->can_use_lcd_text());
 
     LayerTreeHostTest::SetupTree();
   }
@@ -2377,33 +2346,17 @@
   void DidCommitAndDrawFrame() override {
     switch (layer_tree_host()->source_frame_number()) {
       case 1:
-        // The first update consists of a paint of the whole layer.
-        EXPECT_EQ(1, client_.paint_count());
-        // LCD text must have been enabled on the layer.
-        EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
         PostSetNeedsCommitToMainThread();
         break;
       case 2:
-        // Since nothing changed on layer, there should be no paint.
-        EXPECT_EQ(1, client_.paint_count());
-        // LCD text must not have changed.
-        EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
         // Change layer opacity that should trigger lcd change.
         layer_tree_host()->root_layer()->SetOpacity(.5f);
         break;
       case 3:
-        // LCD text doesn't require re-recording, so no painting should occur.
-        EXPECT_EQ(1, client_.paint_count());
-        // LCD text must have been disabled on the layer due to opacity.
-        EXPECT_FALSE(layer_tree_host()->root_layer()->can_use_lcd_text());
         // Change layer opacity that should not trigger lcd change.
         layer_tree_host()->root_layer()->SetOpacity(1.f);
         break;
       case 4:
-        // LCD text doesn't require re-recording, so no painting should occur.
-        EXPECT_EQ(1, client_.paint_count());
-        // Even though LCD text could be allowed.
-        EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
         EndTest();
         break;
     }
@@ -2415,22 +2368,34 @@
   }
 
   void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
+    PictureLayerImpl* root_layer =
+        static_cast<PictureLayerImpl*>(host_impl->active_tree()->root_layer());
+    bool can_use_lcd_text =
+        host_impl->active_tree()->root_layer()->can_use_lcd_text();
     switch (host_impl->active_tree()->source_frame_number()) {
       case 0:
         // The first draw.
         EXPECT_EQ(1, num_tiles_rastered_);
+        EXPECT_TRUE(can_use_lcd_text);
+        EXPECT_TRUE(root_layer->RasterSourceUsesLCDText());
         break;
       case 1:
         // Nothing changed on the layer.
         EXPECT_EQ(1, num_tiles_rastered_);
+        EXPECT_TRUE(can_use_lcd_text);
+        EXPECT_TRUE(root_layer->RasterSourceUsesLCDText());
         break;
       case 2:
-        // LCD text was disabled, it should be re-rastered with LCD text off.
+        // LCD text was disabled; it should be re-rastered with LCD text off.
         EXPECT_EQ(2, num_tiles_rastered_);
+        EXPECT_FALSE(can_use_lcd_text);
+        EXPECT_FALSE(root_layer->RasterSourceUsesLCDText());
         break;
       case 3:
-        // LCD text was enabled but it's sticky and stays off.
+        // LCD text was enabled, but it's sticky and stays off.
         EXPECT_EQ(2, num_tiles_rastered_);
+        EXPECT_TRUE(can_use_lcd_text);
+        EXPECT_FALSE(root_layer->RasterSourceUsesLCDText());
         break;
     }
   }
@@ -2438,7 +2403,7 @@
   void AfterTest() override {}
 
  private:
-  PaintClient client_;
+  FakeContentLayerClient client_;
   int num_tiles_rastered_;
 };
 
@@ -6296,4 +6261,83 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoTasksBetweenWillAndDidCommit);
 
+// Verify that if a LayerImpl holds onto a copy request for multiple
+// frames that it will continue to have a render surface through
+// multiple commits, even though the Layer itself has no reason
+// to have a render surface.
+class LayerPreserveRenderSurfaceFromOutputRequests : public LayerTreeHostTest {
+ protected:
+  void SetupTree() override {
+    scoped_refptr<Layer> root = Layer::Create();
+    root->CreateRenderSurface();
+    root->SetBounds(gfx::Size(10, 10));
+    child_ = Layer::Create();
+    child_->SetBounds(gfx::Size(20, 20));
+    root->AddChild(child_);
+
+    layer_tree_host()->SetRootLayer(root);
+    LayerTreeHostTest::SetupTree();
+  }
+
+  static void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {}
+
+  void BeginTest() override {
+    child_->RequestCopyOfOutput(
+        CopyOutputRequest::CreateBitmapRequest(base::Bind(CopyOutputCallback)));
+    EXPECT_TRUE(child_->HasCopyRequest());
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void DidCommit() override { EXPECT_FALSE(child_->HasCopyRequest()); }
+
+  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
+    LayerImpl* child_impl = host_impl->sync_tree()->LayerById(child_->id());
+
+    switch (host_impl->sync_tree()->source_frame_number()) {
+      case 0:
+        EXPECT_TRUE(child_impl->HasCopyRequest());
+        EXPECT_TRUE(child_impl->render_surface());
+        break;
+      case 1:
+        if (host_impl->proxy()->CommitToActiveTree()) {
+          EXPECT_TRUE(child_impl->HasCopyRequest());
+          EXPECT_TRUE(child_impl->render_surface());
+        } else {
+          EXPECT_FALSE(child_impl->HasCopyRequest());
+          EXPECT_FALSE(child_impl->render_surface());
+        }
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+    LayerImpl* child_impl = host_impl->active_tree()->LayerById(child_->id());
+    EXPECT_TRUE(child_impl->HasCopyRequest());
+    EXPECT_TRUE(child_impl->render_surface());
+
+    switch (host_impl->active_tree()->source_frame_number()) {
+      case 0:
+        // Lose output surface to prevent drawing and cause another commit.
+        host_impl->DidLoseOutputSurface();
+        break;
+      case 1:
+        EndTest();
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  void AfterTest() override {}
+
+ private:
+  scoped_refptr<Layer> child_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerPreserveRenderSurfaceFromOutputRequests);
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index e36f6a0..b8d0320 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -130,7 +130,7 @@
 
     LayerAnimationController* controller =
         layer_tree_host()->root_layer()->layer_animation_controller();
-    Animation* animation = controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     if (animation)
       controller->RemoveAnimation(animation->id());
 
@@ -471,8 +471,7 @@
     LayerAnimationController* controller_impl =
         host_impl->active_tree()->root_layer()->children()[0]->
         layer_animation_controller();
-    Animation* animation =
-        controller_impl->GetAnimation(Animation::Opacity);
+    Animation* animation = controller_impl->GetAnimation(Animation::OPACITY);
     if (!animation)
       return;
 
@@ -523,8 +522,7 @@
     LayerAnimationController* controller =
         layer_tree_host()->root_layer()->children()[0]->
         layer_animation_controller();
-    Animation* animation =
-        controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     main_start_time_ = animation->start_time();
     controller->RemoveAnimation(animation->id());
     EndTest();
@@ -535,8 +533,7 @@
     LayerAnimationController* controller =
         impl_host->active_tree()->root_layer()->children()[0]->
         layer_animation_controller();
-    Animation* animation =
-        controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     if (!animation)
       return;
 
@@ -573,8 +570,7 @@
                                int group) override {
     LayerAnimationController* controller =
         layer_tree_host()->root_layer()->layer_animation_controller();
-    Animation* animation =
-        controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     if (animation)
       controller->RemoveAnimation(animation->id());
     EndTest();
@@ -609,7 +605,7 @@
     LayerAnimationController* controller_impl =
         host_impl->active_tree()->root_layer()->layer_animation_controller();
     Animation* animation_impl =
-        controller_impl->GetAnimation(Animation::Opacity);
+        controller_impl->GetAnimation(Animation::OPACITY);
     controller_impl->RemoveAnimation(animation_impl->id());
     EndTest();
   }
@@ -648,8 +644,7 @@
       // Any valid AnimationCurve will do here.
       scoped_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve());
       scoped_ptr<Animation> animation(
-          Animation::Create(curve.Pass(), 1, 1,
-                                  Animation::Opacity));
+          Animation::Create(curve.Pass(), 1, 1, Animation::OPACITY));
       layer->layer_animation_controller()->AddAnimation(animation.Pass());
 
       // We add the animation *before* attaching the layer to the tree.
@@ -986,7 +981,7 @@
                 gfx::ScrollOffset(500.f, 550.f),
                 EaseInOutTimingFunction::Create()));
         scoped_ptr<Animation> animation(
-            Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+            Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
         animation->set_needs_synchronized_start_time(true);
         bool animation_added = scroll_layer_->AddAnimation(animation.Pass());
         bool impl_scrolling_supported =
@@ -1036,7 +1031,7 @@
         ScrollOffsetAnimationCurve::Create(gfx::ScrollOffset(6500.f, 7500.f),
                                            EaseInOutTimingFunction::Create()));
     scoped_ptr<Animation> animation(
-        Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+        Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
     animation->set_needs_synchronized_start_time(true);
     scroll_layer_->AddAnimation(animation.Pass());
   }
@@ -1050,7 +1045,7 @@
       case 1: {
         Animation* animation =
             scroll_layer_->layer_animation_controller()->GetAnimation(
-                Animation::ScrollOffset);
+                Animation::SCROLL_OFFSET);
         scroll_layer_->layer_animation_controller()->RemoveAnimation(
             animation->id());
         scroll_layer_->SetScrollOffset(final_postion_);
@@ -1080,9 +1075,9 @@
         host_impl->active_tree()->root_layer()->children()[0];
     Animation* animation =
         scroll_layer_impl->layer_animation_controller()->GetAnimation(
-            Animation::ScrollOffset);
+            Animation::SCROLL_OFFSET);
 
-    if (!animation || animation->run_state() != Animation::Running) {
+    if (!animation || animation->run_state() != Animation::RUNNING) {
       host_impl->BlockNotifyReadyToActivateForTesting(false);
       return;
     }
@@ -1187,20 +1182,20 @@
     LayerAnimationController* root_controller_impl =
         host_impl->active_tree()->root_layer()->layer_animation_controller();
     Animation* root_animation =
-        root_controller_impl->GetAnimation(Animation::Opacity);
-    if (!root_animation || root_animation->run_state() != Animation::Running)
+        root_controller_impl->GetAnimation(Animation::OPACITY);
+    if (!root_animation || root_animation->run_state() != Animation::RUNNING)
       return;
 
     LayerAnimationController* child_controller_impl =
         host_impl->active_tree()->root_layer()->children()
             [0]->layer_animation_controller();
     Animation* child_animation =
-        child_controller_impl->GetAnimation(Animation::Opacity);
-    EXPECT_EQ(Animation::Running, child_animation->run_state());
+        child_controller_impl->GetAnimation(Animation::OPACITY);
+    EXPECT_EQ(Animation::RUNNING, child_animation->run_state());
     EXPECT_EQ(root_animation->start_time(), child_animation->start_time());
-    root_controller_impl->AbortAnimations(Animation::Opacity);
-    root_controller_impl->AbortAnimations(Animation::Transform);
-    child_controller_impl->AbortAnimations(Animation::Opacity);
+    root_controller_impl->AbortAnimations(Animation::OPACITY);
+    root_controller_impl->AbortAnimations(Animation::TRANSFORM);
+    child_controller_impl->AbortAnimations(Animation::OPACITY);
     EndTest();
   }
 
@@ -1257,10 +1252,10 @@
            ++iter) {
         int id = ((*iter).second->id());
         if (id == host_impl->RootLayer()->id()) {
-          Animation* anim = (*iter).second->GetAnimation(Animation::Transform);
+          Animation* anim = (*iter).second->GetAnimation(Animation::TRANSFORM);
           EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
         } else if (id == host_impl->RootLayer()->children()[0]->id()) {
-          Animation* anim = (*iter).second->GetAnimation(Animation::Opacity);
+          Animation* anim = (*iter).second->GetAnimation(Animation::OPACITY);
           EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
         }
       }
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index fb0c9c8..cc536f4 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -993,10 +993,8 @@
 
     ResourceProvider::ResourceId resource =
         child_resource_provider_->CreateResource(
-            gfx::Size(4, 4),
-            GL_CLAMP_TO_EDGE,
-            ResourceProvider::TextureHintImmutable,
-            RGBA_8888);
+            gfx::Size(4, 4), GL_CLAMP_TO_EDGE,
+            ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
     ResourceProvider::ScopedWriteLockGL lock(child_resource_provider_.get(),
                                              resource);
 
diff --git a/cc/trees/layer_tree_host_unittest_no_message_loop.cc b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
index d2014ea..06b3674 100644
--- a/cc/trees/layer_tree_host_unittest_no_message_loop.cc
+++ b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
@@ -56,6 +56,7 @@
   // LayerTreeHostClient overrides.
   void WillBeginMainFrame() override {}
   void BeginMainFrame(const BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void DidBeginMainFrame() override {}
   void Layout() override {}
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 03a0860..547ece7 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -582,12 +582,12 @@
     EXPECT_EQ(device_scale_factor_, impl->active_tree()->device_scale_factor());
     switch (impl->active_tree()->source_frame_number()) {
       case 0: {
-        // Gesture scroll on impl thread.
+        // GESTURE scroll on impl thread.
         InputHandler::ScrollStatus status = impl->ScrollBegin(
             gfx::ToCeiledPoint(expected_scroll_layer_impl->position() -
                                gfx::Vector2dF(0.5f, 0.5f)),
-            InputHandler::Gesture);
-        EXPECT_EQ(InputHandler::ScrollStarted, status);
+            InputHandler::GESTURE);
+        EXPECT_EQ(InputHandler::SCROLL_STARTED, status);
         impl->ScrollBy(gfx::Point(), scroll_amount_);
         impl->ScrollEnd();
 
@@ -599,12 +599,12 @@
         break;
       }
       case 1: {
-        // Wheel scroll on impl thread.
+        // WHEEL scroll on impl thread.
         InputHandler::ScrollStatus status = impl->ScrollBegin(
             gfx::ToCeiledPoint(expected_scroll_layer_impl->position() +
                                gfx::Vector2dF(0.5f, 0.5f)),
-            InputHandler::Wheel);
-        EXPECT_EQ(InputHandler::ScrollStarted, status);
+            InputHandler::WHEEL);
+        EXPECT_EQ(InputHandler::SCROLL_STARTED, status);
         impl->ScrollBy(gfx::Point(), scroll_amount_);
         impl->ScrollEnd();
 
@@ -1040,23 +1040,23 @@
     scroll_layer->SetBounds(
         gfx::Size(root->bounds().width() + 100, root->bounds().height() + 100));
     EXPECT_EQ(
-        InputHandler::ScrollStarted,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture,
-                                ScrollBlocksOnNone));
+        InputHandler::SCROLL_STARTED,
+        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
+                                SCROLL_BLOCKS_ON_NONE));
 
     // Set max_scroll_offset = (0, 0).
     scroll_layer->SetBounds(root->bounds());
     EXPECT_EQ(
-        InputHandler::ScrollIgnored,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture,
-                                ScrollBlocksOnNone));
+        InputHandler::SCROLL_IGNORED,
+        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
+                                SCROLL_BLOCKS_ON_NONE));
 
     // Set max_scroll_offset = (-100, -100).
     scroll_layer->SetBounds(gfx::Size());
     EXPECT_EQ(
-        InputHandler::ScrollIgnored,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture,
-                                ScrollBlocksOnNone));
+        InputHandler::SCROLL_IGNORED,
+        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
+                                SCROLL_BLOCKS_ON_NONE));
 
     EndTest();
   }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 4093c42..e86e785 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -534,20 +534,27 @@
   outer_viewport_scroll_layer_ = NULL;
 }
 
-bool LayerTreeImpl::UpdateDrawProperties() {
+bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) {
   if (!needs_update_draw_properties_)
     return true;
 
-  // For max_texture_size.
+  // Calling UpdateDrawProperties must clear this flag, so there can be no
+  // early outs before this.
+  needs_update_draw_properties_ = false;
+
+  // For max_texture_size.  When the renderer is re-created in
+  // CreateAndSetRenderer, the needs update draw properties flag is set
+  // again.
   if (!layer_tree_host_impl_->renderer())
     return false;
 
+  // Clear this after the renderer early out, as it should still be
+  // possible to hit test even without a renderer.
+  render_surface_layer_list_.clear();
+
   if (!root_layer())
     return false;
 
-  needs_update_draw_properties_ = false;
-  render_surface_layer_list_.clear();
-
   {
     TRACE_EVENT2(
         "cc", "LayerTreeImpl::UpdateDrawProperties::CalculateDrawProperties",
@@ -647,6 +654,25 @@
         occlusion_tracker.ComputeVisibleRegionInScreen();
   }
 
+  // It'd be ideal if this could be done earlier, but when the raster source
+  // is updated from the main thread during push properties, update draw
+  // properties has not occurred yet and so it's not clear whether or not the
+  // layer can or cannot use lcd text.  So, this is the cleanup pass to
+  // determine if the raster source needs to be replaced with a non-lcd
+  // raster source due to draw properties.
+  if (update_lcd_text) {
+    // TODO(enne): Make LTHI::sync_tree return this value.
+    LayerTreeImpl* sync_tree =
+        layer_tree_host_impl_->proxy()->CommitToActiveTree()
+            ? layer_tree_host_impl_->active_tree()
+            : layer_tree_host_impl_->pending_tree();
+    // If this is not the sync tree, then it is not safe to update lcd text
+    // as it causes invalidations and the tiles may be in use.
+    DCHECK_EQ(this, sync_tree);
+    for (const auto& layer : picture_layers_)
+      layer->UpdateCanUseLCDTextAfterCommit();
+  }
+
   {
     TRACE_EVENT_BEGIN2("cc", "LayerTreeImpl::UpdateDrawProperties::UpdateTiles",
                        "IsActive", IsActiveTree(), "SourceFrameNumber",
@@ -859,6 +885,10 @@
   return layer_tree_host_impl_->recycle_tree() == this;
 }
 
+bool LayerTreeImpl::IsSyncTree() const {
+  return layer_tree_host_impl_->sync_tree() == this;
+}
+
 LayerImpl* LayerTreeImpl::FindActiveTreeLayerById(int id) {
   LayerTreeImpl* tree = layer_tree_host_impl_->active_tree();
   if (!tree)
@@ -912,7 +942,7 @@
   base::TimeDelta duration =
       base::TimeDelta::FromMilliseconds(settings().scrollbar_fade_duration_ms);
   switch (settings().scrollbar_animator) {
-    case LayerTreeSettings::LinearFade: {
+    case LayerTreeSettings::LINEAR_FADE: {
       return ScrollbarAnimationControllerLinearFade::Create(
           scrolling_layer,
           layer_tree_host_impl_,
@@ -920,14 +950,14 @@
           resize_delay,
           duration);
     }
-    case LayerTreeSettings::Thinning: {
+    case LayerTreeSettings::THINNING: {
       return ScrollbarAnimationControllerThinning::Create(scrolling_layer,
                                                           layer_tree_host_impl_,
                                                           delay,
                                                           resize_delay,
                                                           duration);
     }
-    case LayerTreeSettings::NoAnimator:
+    case LayerTreeSettings::NO_ANIMATOR:
       NOTREACHED();
       break;
   }
@@ -1161,13 +1191,13 @@
 void LayerTreeImpl::ProcessUIResourceRequestQueue() {
   for (const auto& req : ui_resource_request_queue_) {
     switch (req.GetType()) {
-      case UIResourceRequest::UIResourceCreate:
+      case UIResourceRequest::UI_RESOURCE_CREATE:
         layer_tree_host_impl_->CreateUIResource(req.GetId(), req.GetBitmap());
         break;
-      case UIResourceRequest::UIResourceDelete:
+      case UIResourceRequest::UI_RESOURCE_DELETE:
         layer_tree_host_impl_->DeleteUIResource(req.GetId());
         break;
-      case UIResourceRequest::UIResourceInvalidRequest:
+      case UIResourceRequest::UI_RESOURCE_INVALID_REQUEST:
         NOTREACHED();
         break;
     }
@@ -1461,7 +1491,8 @@
     const gfx::PointF& screen_space_point) {
   if (!root_layer())
     return NULL;
-  if (!UpdateDrawProperties())
+  bool update_lcd_text = false;
+  if (!UpdateDrawProperties(update_lcd_text))
     return NULL;
   FindClosestMatchingLayerDataForRecursion data_for_recursion;
   FindClosestMatchingLayer(screen_space_point,
@@ -1503,7 +1534,8 @@
     const gfx::PointF& screen_space_point) {
   if (!root_layer())
     return NULL;
-  if (!UpdateDrawProperties())
+  bool update_lcd_text = false;
+  if (!UpdateDrawProperties(update_lcd_text))
     return NULL;
   FindWheelEventLayerFunctor func;
   FindClosestMatchingLayerDataForRecursion data_for_recursion;
@@ -1523,7 +1555,8 @@
     const gfx::PointF& screen_space_point) {
   if (!root_layer())
     return NULL;
-  if (!UpdateDrawProperties())
+  bool update_lcd_text = false;
+  if (!UpdateDrawProperties(update_lcd_text))
     return NULL;
   FindTouchEventLayerFunctor func = {screen_space_point};
   FindClosestMatchingLayerDataForRecursion data_for_recursion;
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 54c8660..5935d29 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -87,6 +87,7 @@
   bool IsActiveTree() const;
   bool IsPendingTree() const;
   bool IsRecycleTree() const;
+  bool IsSyncTree() const;
   LayerImpl* FindActiveTreeLayerById(int id);
   LayerImpl* FindPendingTreeLayerById(int id);
   bool PinchGestureActive() const;
@@ -197,8 +198,9 @@
   }
 
   // Updates draw properties and render surface layer list, as well as tile
-  // priorities. Returns false if it was unable to update.
-  bool UpdateDrawProperties();
+  // priorities. Returns false if it was unable to update.  Updating lcd
+  // text may cause invalidations, so should only be done after a commit.
+  bool UpdateDrawProperties(bool update_lcd_text);
 
   void set_needs_update_draw_properties() {
     needs_update_draw_properties_ = true;
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index d3afbe2..9d605ae 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -33,7 +33,7 @@
       gpu_rasterization_skewport_target_time_in_seconds(0.0f),
       threaded_gpu_rasterization_enabled(false),
       create_low_res_tiling(false),
-      scrollbar_animator(NoAnimator),
+      scrollbar_animator(NO_ANIMATOR),
       scrollbar_fade_delay_ms(0),
       scrollbar_fade_resize_delay_ms(0),
       scrollbar_fade_duration_ms(0),
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index fd108f4..a0fbeaa 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -43,9 +43,9 @@
   bool create_low_res_tiling;
 
   enum ScrollbarAnimator {
-    NoAnimator,
-    LinearFade,
-    Thinning,
+    NO_ANIMATOR,
+    LINEAR_FADE,
+    THINNING,
   };
   ScrollbarAnimator scrollbar_animator;
   int scrollbar_fade_delay_ms;
diff --git a/cc/trees/occlusion_tracker_perftest.cc b/cc/trees/occlusion_tracker_perftest.cc
index 4639b9c..e53a05f 100644
--- a/cc/trees/occlusion_tracker_perftest.cc
+++ b/cc/trees/occlusion_tracker_perftest.cc
@@ -93,7 +93,8 @@
   opaque_layer->SetContentBounds(viewport_rect.size());
   active_tree()->root_layer()->AddChild(opaque_layer.Pass());
 
-  active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  active_tree()->UpdateDrawProperties(update_lcd_text);
   const LayerImplList& rsll = active_tree()->RenderSurfaceLayerList();
   ASSERT_EQ(1u, rsll.size());
   EXPECT_EQ(1u, rsll[0]->render_surface()->layer_list().size());
@@ -164,7 +165,8 @@
     active_tree()->root_layer()->AddChild(opaque_layer.Pass());
   }
 
-  active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  active_tree()->UpdateDrawProperties(update_lcd_text);
   const LayerImplList& rsll = active_tree()->RenderSurfaceLayerList();
   ASSERT_EQ(1u, rsll.size());
   EXPECT_EQ(static_cast<size_t>(kNumOpaqueLayers),
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index ae39f03..7fe0494 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -118,7 +118,7 @@
 
   const bool has_animated_transform =
       layer->layer_animation_controller()->IsAnimatingProperty(
-          Animation::Transform);
+          Animation::TRANSFORM);
 
   const bool has_transform_origin = layer->transform_origin() != gfx::Point3F();
 
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 4a079f3..e45b182 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -532,7 +532,8 @@
     DebugScopedSetImplThread impl(const_cast<SingleThreadProxy*>(this));
     if (layer_tree_host_impl_->settings().impl_side_painting) {
       layer_tree_host_impl_->ActivateSyncTree();
-      layer_tree_host_impl_->active_tree()->UpdateDrawProperties();
+      DCHECK(!layer_tree_host_impl_->active_tree()
+                  ->needs_update_draw_properties());
       layer_tree_host_impl_->PrepareTiles();
       layer_tree_host_impl_->SynchronouslyInitializeAllTiles();
     }
@@ -685,6 +686,10 @@
                  weak_factory_.GetWeakPtr()));
 }
 
+void SingleThreadProxy::SendBeginMainFrameNotExpectedSoon() {
+  layer_tree_host_->BeginMainFrameNotExpectedSoon();
+}
+
 void SingleThreadProxy::BeginMainFrame() {
   if (defer_commits_) {
     TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit",
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 52dd876..f463db1 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -79,6 +79,7 @@
   base::TimeDelta CommitToActivateDurationEstimate() override;
   void DidBeginImplFrameDeadline() override;
   void SendBeginFramesToChildren(const BeginFrameArgs& args) override;
+  void SendBeginMainFrameNotExpectedSoon() override;
 
   // LayerTreeHostImplClient implementation
   void UpdateRendererCapabilitiesOnImplThread() override;
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 167cca9..1097805 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -154,9 +154,9 @@
 }
 
 bool ThreadProxy::CommitToActiveTree() const {
-  // With ThreadProxy we use a pending tree and activate it once it's ready to
-  // draw.
-  return false;
+  // With ThreadProxy and impl-side painting, we use a pending tree and activate
+  // it once it's ready to draw.
+  return !impl().layer_tree_host_impl->settings().impl_side_painting;
 }
 
 void ThreadProxy::SetLayerTreeHostClientReady() {
@@ -701,6 +701,12 @@
   impl().timing_history.DidBeginMainFrame();
 }
 
+void ThreadProxy::SendBeginMainFrameNotExpectedSoon() {
+  Proxy::MainThreadTaskRunner()->PostTask(
+      FROM_HERE, base::Bind(&ThreadProxy::BeginMainFrameNotExpectedSoon,
+                            main_thread_weak_ptr_));
+}
+
 void ThreadProxy::BeginMainFrame(
     scoped_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {
   benchmark_instrumentation::ScopedBeginFrameTask begin_frame_task(
@@ -861,6 +867,12 @@
   layer_tree_host()->DidBeginMainFrame();
 }
 
+void ThreadProxy::BeginMainFrameNotExpectedSoon() {
+  TRACE_EVENT0("cc", "ThreadProxy::BeginMainFrameNotExpectedSoon");
+  DCHECK(IsMainThread());
+  layer_tree_host()->BeginMainFrameNotExpectedSoon();
+}
+
 void ThreadProxy::StartCommitOnImplThread(CompletionEvent* completion,
                                           ResourceUpdateQueue* raw_queue) {
   TRACE_EVENT0("cc", "ThreadProxy::StartCommitOnImplThread");
@@ -1021,8 +1033,11 @@
   impl().timing_history.DidStartDrawing();
   base::AutoReset<bool> mark_inside(&impl().inside_draw, true);
 
-  if (impl().layer_tree_host_impl->pending_tree())
-    impl().layer_tree_host_impl->pending_tree()->UpdateDrawProperties();
+  if (impl().layer_tree_host_impl->pending_tree()) {
+    bool update_lcd_text = false;
+    impl().layer_tree_host_impl->pending_tree()->UpdateDrawProperties(
+        update_lcd_text);
+  }
 
   // This method is called on a forced draw, regardless of whether we are able
   // to produce a frame, as the calling site on main thread is blocked until its
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 570d240..8b4c302 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -227,6 +227,7 @@
   base::TimeDelta CommitToActivateDurationEstimate() override;
   void DidBeginImplFrameDeadline() override;
   void SendBeginFramesToChildren(const BeginFrameArgs& args) override;
+  void SendBeginMainFrameNotExpectedSoon() override;
 
   // ResourceUpdateControllerClient implementation
   void ReadyToFinalizeTextureUpdates() override;
@@ -244,6 +245,7 @@
       const RendererCapabilities& capabilities);
   void BeginMainFrame(
       scoped_ptr<BeginMainFrameAndCommitState> begin_main_frame_state);
+  void BeginMainFrameNotExpectedSoon();
   void DidCommitAndDrawFrame();
   void DidCompleteSwapBuffers();
   void SetAnimationEvents(scoped_ptr<AnimationEventsVector> queue);
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index e1d5348..5b39586 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -97,6 +97,8 @@
 
     defines = [ "GL_GLEXT_PROTOTYPES" ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":gpu",
       ":test_support",
@@ -226,6 +228,8 @@
       "config/gpu_util_unittest.cc",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":gpu",
       ":test_support",
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index a36a026..93f3c47 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -40,6 +40,7 @@
 #define glClearColor GLES2_GET_FUN(ClearColor)
 #define glClearDepthf GLES2_GET_FUN(ClearDepthf)
 #define glClearStencil GLES2_GET_FUN(ClearStencil)
+#define glClientWaitSync GLES2_GET_FUN(ClientWaitSync)
 #define glColorMask GLES2_GET_FUN(ColorMask)
 #define glCompileShader GLES2_GET_FUN(CompileShader)
 #define glCompressedTexImage2D GLES2_GET_FUN(CompressedTexImage2D)
@@ -222,6 +223,7 @@
 #define glVertexAttribIPointer GLES2_GET_FUN(VertexAttribIPointer)
 #define glVertexAttribPointer GLES2_GET_FUN(VertexAttribPointer)
 #define glViewport GLES2_GET_FUN(Viewport)
+#define glWaitSync GLES2_GET_FUN(WaitSync)
 #define glBlitFramebufferCHROMIUM GLES2_GET_FUN(BlitFramebufferCHROMIUM)
 #define glRenderbufferStorageMultisampleCHROMIUM \
   GLES2_GET_FUN(RenderbufferStorageMultisampleCHROMIUM)
diff --git a/gpu/PRESUBMIT.py b/gpu/PRESUBMIT.py
deleted file mode 100644
index 53fc153..0000000
--- a/gpu/PRESUBMIT.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Top-level presubmit script for gpu/.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-def GetPreferredTryMasters(project, change):
-  return {
-    'tryserver.chromium.gpu': {
-      'win_gpu': set(['defaulttests']),
-    }
-  }
diff --git a/gpu/blink/BUILD.gn b/gpu/blink/BUILD.gn
index 464061c..7d08ba1 100644
--- a/gpu/blink/BUILD.gn
+++ b/gpu/blink/BUILD.gn
@@ -14,6 +14,9 @@
     "webgraphicscontext3d_in_process_command_buffer_impl.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GPU_BLINK_IMPLEMENTATION" ]
 
   deps = [
@@ -33,9 +36,4 @@
     "//ui/gfx",
     "//ui/gfx/geometry",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index ca39150..141d259 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1429,6 +1429,17 @@
       '1',
     ],
   },
+  'SyncFlushFlags': {
+    'type': 'GLbitfield',
+    'is_complete': True,
+    'valid': [
+      'GL_SYNC_FLUSH_COMMANDS_BIT',
+      '0',
+    ],
+    'invalid': [
+      '0xFFFFFFFF',
+    ],
+  },
 }
 
 # This table specifies the different pepper interfaces that are supported for
@@ -1642,6 +1653,14 @@
       '0': '0.5f'
     },
   },
+  'ClientWaitSync': {
+    'type': 'Custom',
+    'data_transfer_methods': ['shm'],
+    'cmd_args': 'GLuint sync, GLbitfieldSyncFlushFlags flags, '
+                'GLuint timeout_0, GLuint timeout_1, GLenum* result',
+    'unsafe': True,
+    'result': ['GLenum'],
+  },
   'ColorMask': {
     'type': 'StateSet',
     'state': 'ColorMask',
@@ -2909,6 +2928,14 @@
                 'GLsizei stride, GLuint offset',
     'client_test': False,
   },
+  'WaitSync': {
+    'type': 'Custom',
+    'cmd_args': 'GLuint sync, GLbitfieldSyncFlushFlags flags, '
+                'GLuint timeout_0, GLuint timeout_1',
+    'impl_func': False,
+    'client_test': False,
+    'unsafe': True,
+  },
   'Scissor': {
     'type': 'StateSet',
     'state': 'Scissor',
@@ -8971,11 +8998,12 @@
     # Forward declaration of a few enums used in constant argument
     # to avoid including GL header files.
     enum_defines = {
-        'GL_SYNC_GPU_COMMANDS_COMPLETE': 0x9117,
+        'GL_SYNC_GPU_COMMANDS_COMPLETE': '0x9117',
+        'GL_SYNC_FLUSH_COMMANDS_BIT': '0x00000001',
       }
     file.Write('\n')
     for enum in enum_defines:
-      file.Write("#define %s 0x%x\n" % (enum, enum_defines[enum]))
+      file.Write("#define %s %s\n" % (enum, enum_defines[enum]))
     file.Write('\n')
     for func in self.functions:
       if True:
diff --git a/gpu/command_buffer/client/BUILD.gn b/gpu/command_buffer/client/BUILD.gn
index 2d28d1a..a5532cb 100644
--- a/gpu/command_buffer/client/BUILD.gn
+++ b/gpu/command_buffer/client/BUILD.gn
@@ -19,10 +19,8 @@
 
   defines = [ "GPU_IMPLEMENTATION" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   all_dependent_configs = [ "//third_party/khronos:khronos_headers" ]
 
@@ -56,10 +54,8 @@
 
   defines = [ "GPU_IMPLEMENTATION" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":client",
@@ -129,14 +125,12 @@
 component("gles2_implementation") {
   sources = gles2_implementation_source_files
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GLES2_IMPL_IMPLEMENTATION" ]
   all_dependent_configs = [ "//third_party/khronos:khronos_headers" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
-
   deps = [
     ":gles2_cmd_helper",
     ":gles2_interface",
@@ -171,10 +165,8 @@
   sources = gles2_c_lib_source_files
   defines = [ "GLES2_C_LIB_IMPLEMENTATION" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":client",
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 23d3d6e..803e66f 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -119,6 +119,9 @@
 void GLES2ClearStencil(GLint s) {
   gles2::GetGLContext()->ClearStencil(s);
 }
+GLenum GLES2ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  return gles2::GetGLContext()->ClientWaitSync(sync, flags, timeout);
+}
 void GLES2ColorMask(GLboolean red,
                     GLboolean green,
                     GLboolean blue,
@@ -929,6 +932,9 @@
 void GLES2Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
   gles2::GetGLContext()->Viewport(x, y, width, height);
 }
+void GLES2WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  gles2::GetGLContext()->WaitSync(sync, flags, timeout);
+}
 void GLES2BlitFramebufferCHROMIUM(GLint srcX0,
                                   GLint srcY0,
                                   GLint srcX1,
@@ -1421,6 +1427,10 @@
      reinterpret_cast<GLES2FunctionPointer>(glClearStencil),
     },
     {
+     "glClientWaitSync",
+     reinterpret_cast<GLES2FunctionPointer>(glClientWaitSync),
+    },
+    {
      "glColorMask",
      reinterpret_cast<GLES2FunctionPointer>(glColorMask),
     },
@@ -2146,6 +2156,10 @@
      reinterpret_cast<GLES2FunctionPointer>(glViewport),
     },
     {
+     "glWaitSync",
+     reinterpret_cast<GLES2FunctionPointer>(glWaitSync),
+    },
+    {
      "glBlitFramebufferCHROMIUM",
      reinterpret_cast<GLES2FunctionPointer>(glBlitFramebufferCHROMIUM),
     },
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index bda8bb3..c58ce2c 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -240,6 +240,19 @@
   }
 }
 
+void ClientWaitSync(GLuint sync,
+                    GLbitfield flags,
+                    GLuint timeout_0,
+                    GLuint timeout_1,
+                    uint32_t result_shm_id,
+                    uint32_t result_shm_offset) {
+  gles2::cmds::ClientWaitSync* c = GetCmdSpace<gles2::cmds::ClientWaitSync>();
+  if (c) {
+    c->Init(sync, flags, timeout_0, timeout_1, result_shm_id,
+            result_shm_offset);
+  }
+}
+
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -1982,6 +1995,16 @@
   }
 }
 
+void WaitSync(GLuint sync,
+              GLbitfield flags,
+              GLuint timeout_0,
+              GLuint timeout_1) {
+  gles2::cmds::WaitSync* c = GetCmdSpace<gles2::cmds::WaitSync>();
+  if (c) {
+    c->Init(sync, flags, timeout_0, timeout_1);
+  }
+}
+
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index e853766..2dd95ec 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -4930,6 +4930,39 @@
   CheckGLError();
 }
 
+GLenum GLES2Implementation::ClientWaitSync(
+    GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glClientWaitSync(" << sync
+                 << ", " << flags << ", " << timeout << ")");
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = GetResultAs<Result*>();
+  if (!result) {
+    SetGLError(GL_OUT_OF_MEMORY, "ClientWaitSync", "");
+    return GL_WAIT_FAILED;
+  }
+  *result = GL_WAIT_FAILED;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(timeout, &v32_0, &v32_1);
+  helper_->ClientWaitSync(
+      ToGLuint(sync), flags, v32_0, v32_1,
+      GetResultShmId(), GetResultShmOffset());
+  WaitForCmd();
+  GPU_CLIENT_LOG("returned " << *result);
+  CheckGLError();
+  return *result;
+}
+
+void GLES2Implementation::WaitSync(
+    GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glWaitSync(" << sync << ", "
+                 << flags << ", " << timeout << ")");
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(timeout, &v32_0, &v32_1);
+  helper_->WaitSync(ToGLuint(sync), flags, v32_0, v32_1);
+  CheckGLError();
+}
 
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 9d2d8eb..5c994e3 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -97,6 +97,8 @@
 
 void ClearStencil(GLint s) override;
 
+GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -702,6 +704,8 @@
 
 void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
 
+void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index 52a2cef..2acbc63 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -3583,7 +3583,7 @@
   expected.cmd.Init(kCap, result1.id, result1.offset);
 
   EXPECT_CALL(*command_buffer(), OnFlush())
-      .WillOnce(SetMemory(result1.ptr, uint32_t(kCap)))
+      .WillOnce(SetMemory(result1.ptr, uint32_t(GL_TRUE)))
       .RetiresOnSaturation();
 
   GLboolean result = gl_->IsEnabled(kCap);
@@ -3591,6 +3591,47 @@
   EXPECT_TRUE(result);
 }
 
+TEST_F(GLES2ImplementationTest, ClientWaitSync) {
+  const GLuint client_sync_id = 36;
+  struct Cmds {
+    cmds::ClientWaitSync cmd;
+  };
+
+  Cmds expected;
+  ExpectedMemoryInfo result1 =
+      GetExpectedResultMemory(sizeof(cmds::ClientWaitSync::Result));
+  const GLuint64 kTimeout = 0xABCDEF0123456789;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  expected.cmd.Init(client_sync_id, GL_SYNC_FLUSH_COMMANDS_BIT,
+                    v32_0, v32_1, result1.id, result1.offset);
+
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillOnce(SetMemory(result1.ptr, uint32_t(GL_CONDITION_SATISFIED)))
+      .RetiresOnSaturation();
+
+  GLenum result = gl_->ClientWaitSync(
+      reinterpret_cast<GLsync>(client_sync_id), GL_SYNC_FLUSH_COMMANDS_BIT,
+      kTimeout);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+  EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), result);
+}
+
+TEST_F(GLES2ImplementationTest, WaitSync) {
+  const GLuint kClientSyncId = 36;
+  struct Cmds {
+    cmds::WaitSync cmd;
+  };
+  Cmds expected;
+  const GLuint64 kTimeout = GL_TIMEOUT_IGNORED;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  expected.cmd.Init(kClientSyncId, 0, v32_0, v32_1);
+
+  gl_->WaitSync(reinterpret_cast<GLsync>(kClientSyncId), 0, kTimeout);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
 TEST_F(GLES2ImplementationManualInitTest, LoseContextOnOOM) {
   ContextInitOptions init_options;
   init_options.lose_context_when_out_of_memory = true;
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index 8b02ad9..8149a96 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -287,6 +287,7 @@
   gl_->ClearStencil(1);
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
+// TODO(zmo): Implement unit test for ClientWaitSync
 
 TEST_F(GLES2ImplementationTest, ColorMask) {
   struct Cmds {
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 990e701..623c413 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -70,6 +70,9 @@
                         GLclampf alpha) = 0;
 virtual void ClearDepthf(GLclampf depth) = 0;
 virtual void ClearStencil(GLint s) = 0;
+virtual GLenum ClientWaitSync(GLsync sync,
+                              GLbitfield flags,
+                              GLuint64 timeout) = 0;
 virtual void ColorMask(GLboolean red,
                        GLboolean green,
                        GLboolean blue,
@@ -514,6 +517,7 @@
                                  GLsizei stride,
                                  const void* ptr) = 0;
 virtual void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) = 0;
+virtual void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) = 0;
 virtual void BlitFramebufferCHROMIUM(GLint srcX0,
                                      GLint srcY0,
                                      GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index f020e14..31d9636 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -69,6 +69,7 @@
                 GLclampf alpha) override;
 void ClearDepthf(GLclampf depth) override;
 void ClearStencil(GLint s) override;
+GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -501,6 +502,7 @@
                          GLsizei stride,
                          const void* ptr) override;
 void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
+void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index 15f2912..d7aa072 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -105,6 +105,11 @@
 }
 void GLES2InterfaceStub::ClearStencil(GLint /* s */) {
 }
+GLenum GLES2InterfaceStub::ClientWaitSync(GLsync /* sync */,
+                                          GLbitfield /* flags */,
+                                          GLuint64 /* timeout */) {
+  return 0;
+}
 void GLES2InterfaceStub::ColorMask(GLboolean /* red */,
                                    GLboolean /* green */,
                                    GLboolean /* blue */,
@@ -864,6 +869,10 @@
                                   GLsizei /* width */,
                                   GLsizei /* height */) {
 }
+void GLES2InterfaceStub::WaitSync(GLsync /* sync */,
+                                  GLbitfield /* flags */,
+                                  GLuint64 /* timeout */) {
+}
 void GLES2InterfaceStub::BlitFramebufferCHROMIUM(GLint /* srcX0 */,
                                                  GLint /* srcY0 */,
                                                  GLint /* srcX1 */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 66deca4..33626f7 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -69,6 +69,7 @@
                 GLclampf alpha) override;
 void ClearDepthf(GLclampf depth) override;
 void ClearStencil(GLint s) override;
+GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -501,6 +502,7 @@
                          GLsizei stride,
                          const void* ptr) override;
 void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
+void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 79fd529..8e6faa7 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -183,6 +183,13 @@
   gl_->ClearStencil(s);
 }
 
+GLenum GLES2TraceImplementation::ClientWaitSync(GLsync sync,
+                                                GLbitfield flags,
+                                                GLuint64 timeout) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::ClientWaitSync");
+  return gl_->ClientWaitSync(sync, flags, timeout);
+}
+
 void GLES2TraceImplementation::ColorMask(GLboolean red,
                                          GLboolean green,
                                          GLboolean blue,
@@ -1467,6 +1474,13 @@
   gl_->Viewport(x, y, width, height);
 }
 
+void GLES2TraceImplementation::WaitSync(GLsync sync,
+                                        GLbitfield flags,
+                                        GLuint64 timeout) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::WaitSync");
+  gl_->WaitSync(sync, flags, timeout);
+}
+
 void GLES2TraceImplementation::BlitFramebufferCHROMIUM(GLint srcX0,
                                                        GLint srcY0,
                                                        GLint srcX1,
diff --git a/gpu/command_buffer/client/program_info_manager.cc b/gpu/command_buffer/client/program_info_manager.cc
index 779d914..89e8913 100644
--- a/gpu/command_buffer/client/program_info_manager.cc
+++ b/gpu/command_buffer/client/program_info_manager.cc
@@ -332,11 +332,10 @@
   if (!link_status_) {
     return;
   }
-  attrib_infos_.clear();
-  uniform_infos_.clear();
-  frag_data_locations_.clear();
-  max_attrib_name_length_ = 0;
-  max_uniform_name_length_ = 0;
+  DCHECK_EQ(0u, attrib_infos_.size());
+  DCHECK_EQ(0u, uniform_infos_.size());
+  DCHECK_EQ(0, max_attrib_name_length_);
+  DCHECK_EQ(0, max_uniform_name_length_);
   const ProgramInput* inputs = LocalGetAs<const ProgramInput*>(
       result, sizeof(*header),
       sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms));
@@ -382,8 +381,8 @@
     // This should only happen on a lost context.
     return;
   }
-  uniform_blocks_.clear();
-  active_uniform_block_max_name_length_ = 0;
+  DCHECK_EQ(0u, uniform_blocks_.size());
+  DCHECK_EQ(0u, active_uniform_block_max_name_length_);
 
   // |result| comes from GPU process. We consider it trusted data. Therefore,
   // no need to check for overflows as the GPU side did the checks already.
@@ -453,7 +452,7 @@
     // This should only happen on a lost context.
     return;
   }
-  uniforms_es3_.clear();
+  DCHECK_EQ(0u, uniforms_es3_.size());
 
   // |result| comes from GPU process. We consider it trusted data. Therefore,
   // no need to check for overflows as the GPU side did the checks already.
@@ -495,8 +494,8 @@
     // This should only happen on a lost context.
     return;
   }
-  transform_feedback_varyings_.clear();
-  transform_feedback_varying_max_length_ = 0;
+  DCHECK_EQ(0u, transform_feedback_varyings_.size());
+  DCHECK_EQ(0u, transform_feedback_varying_max_length_);
 
   // |result| comes from GPU process. We consider it trusted data. Therefore,
   // no need to check for overflows as the GPU side did the checks already.
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt
index 3e8d902..570eff1 100644
--- a/gpu/command_buffer/cmd_buffer_functions.txt
+++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -31,6 +31,7 @@
 GL_APICALL void         GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
 GL_APICALL void         GL_APIENTRY glClearDepthf (GLclampf depth);
 GL_APICALL void         GL_APIENTRY glClearStencil (GLint s);
+GL_APICALL GLenum       GL_APIENTRY glClientWaitSync (GLsync sync, GLbitfieldSyncFlushFlags flags, GLuint64 timeout);
 GL_APICALL void         GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
 GL_APICALL void         GL_APIENTRY glCompileShader (GLidShader shader);
 GL_APICALL void         GL_APIENTRY glCompressedTexImage2D (GLenumTextureTarget target, GLint level, GLenumCompressedTextureFormat internalformat, GLsizei width, GLsizei height, GLintTextureBorder border, GLsizei imageSize, const void* data);
@@ -212,6 +213,7 @@
 GL_APICALL void         GL_APIENTRY glVertexAttribIPointer (GLuint indx, GLintVertexAttribSize size, GLenumVertexAttribType type, GLsizei stride, const void* ptr);
 GL_APICALL void         GL_APIENTRY glVertexAttribPointer (GLuint indx, GLintVertexAttribSize size, GLenumVertexAttribType type, GLboolean normalized, GLsizei stride, const void* ptr);
 GL_APICALL void         GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
+GL_APICALL void         GL_APIENTRY glWaitSync (GLsync sync, GLbitfieldSyncFlushFlags flags, GLuint64 timeout);
 GL_APICALL void         GL_APIENTRY glBlitFramebufferCHROMIUM (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenumBlitFilter filter);
 GL_APICALL void         GL_APIENTRY glRenderbufferStorageMultisampleCHROMIUM (GLenumRenderBufferTarget target, GLsizei samples, GLenumRenderBufferFormat internalformat, GLsizei width, GLsizei height);
 GL_APICALL void         GL_APIENTRY glRenderbufferStorageMultisampleEXT (GLenumRenderBufferTarget target, GLsizei samples, GLenumRenderBufferFormat internalformat, GLsizei width, GLsizei height);
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h
index 944edfd..d0faf14 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.h
+++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -43,6 +43,7 @@
 typedef khronos_intptr_t GLintptr;
 typedef khronos_ssize_t  GLsizeiptr;
 typedef struct __GLsync *GLsync;
+typedef uint64_t GLuint64;
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index cb88581..76ca3dd 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -11,6 +11,7 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
 
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
 
 struct ActiveTexture {
@@ -1169,6 +1170,73 @@
 static_assert(offsetof(ClearStencil, s) == 4,
               "offset of ClearStencil s should be 4");
 
+struct ClientWaitSync {
+  typedef ClientWaitSync ValueType;
+  static const CommandId kCmdId = kClientWaitSync;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  typedef GLenum Result;
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1,
+            uint32_t _result_shm_id,
+            uint32_t _result_shm_offset) {
+    SetHeader();
+    sync = _sync;
+    flags = _flags;
+    timeout_0 = _timeout_0;
+    timeout_1 = _timeout_1;
+    result_shm_id = _result_shm_id;
+    result_shm_offset = _result_shm_offset;
+  }
+
+  void* Set(void* cmd,
+            GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1,
+            uint32_t _result_shm_id,
+            uint32_t _result_shm_offset) {
+    static_cast<ValueType*>(cmd)->Init(_sync, _flags, _timeout_0, _timeout_1,
+                                       _result_shm_id, _result_shm_offset);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t sync;
+  uint32_t flags;
+  uint32_t timeout_0;
+  uint32_t timeout_1;
+  uint32_t result_shm_id;
+  uint32_t result_shm_offset;
+};
+
+static_assert(sizeof(ClientWaitSync) == 28,
+              "size of ClientWaitSync should be 28");
+static_assert(offsetof(ClientWaitSync, header) == 0,
+              "offset of ClientWaitSync header should be 0");
+static_assert(offsetof(ClientWaitSync, sync) == 4,
+              "offset of ClientWaitSync sync should be 4");
+static_assert(offsetof(ClientWaitSync, flags) == 8,
+              "offset of ClientWaitSync flags should be 8");
+static_assert(offsetof(ClientWaitSync, timeout_0) == 12,
+              "offset of ClientWaitSync timeout_0 should be 12");
+static_assert(offsetof(ClientWaitSync, timeout_1) == 16,
+              "offset of ClientWaitSync timeout_1 should be 16");
+static_assert(offsetof(ClientWaitSync, result_shm_id) == 20,
+              "offset of ClientWaitSync result_shm_id should be 20");
+static_assert(offsetof(ClientWaitSync, result_shm_offset) == 24,
+              "offset of ClientWaitSync result_shm_offset should be 24");
+
 struct ColorMask {
   typedef ColorMask ValueType;
   static const CommandId kCmdId = kColorMask;
@@ -9786,6 +9854,57 @@
 static_assert(offsetof(Viewport, height) == 16,
               "offset of Viewport height should be 16");
 
+struct WaitSync {
+  typedef WaitSync ValueType;
+  static const CommandId kCmdId = kWaitSync;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1) {
+    SetHeader();
+    sync = _sync;
+    flags = _flags;
+    timeout_0 = _timeout_0;
+    timeout_1 = _timeout_1;
+  }
+
+  void* Set(void* cmd,
+            GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1) {
+    static_cast<ValueType*>(cmd)->Init(_sync, _flags, _timeout_0, _timeout_1);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t sync;
+  uint32_t flags;
+  uint32_t timeout_0;
+  uint32_t timeout_1;
+};
+
+static_assert(sizeof(WaitSync) == 20, "size of WaitSync should be 20");
+static_assert(offsetof(WaitSync, header) == 0,
+              "offset of WaitSync header should be 0");
+static_assert(offsetof(WaitSync, sync) == 4,
+              "offset of WaitSync sync should be 4");
+static_assert(offsetof(WaitSync, flags) == 8,
+              "offset of WaitSync flags should be 8");
+static_assert(offsetof(WaitSync, timeout_0) == 12,
+              "offset of WaitSync timeout_0 should be 12");
+static_assert(offsetof(WaitSync, timeout_1) == 16,
+              "offset of WaitSync timeout_1 should be 16");
+
 struct BlitFramebufferCHROMIUM {
   typedef BlitFramebufferCHROMIUM ValueType;
   static const CommandId kCmdId = kBlitFramebufferCHROMIUM;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index a89683a..9422405 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -394,6 +394,24 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, ClientWaitSync) {
+  cmds::ClientWaitSync& cmd = *GetBufferAs<cmds::ClientWaitSync>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLbitfield>(12),
+              static_cast<GLuint>(13), static_cast<GLuint>(14),
+              static_cast<uint32_t>(15), static_cast<uint32_t>(16));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::ClientWaitSync::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.sync);
+  EXPECT_EQ(static_cast<GLbitfield>(12), cmd.flags);
+  EXPECT_EQ(static_cast<GLuint>(13), cmd.timeout_0);
+  EXPECT_EQ(static_cast<GLuint>(14), cmd.timeout_1);
+  EXPECT_EQ(static_cast<uint32_t>(15), cmd.result_shm_id);
+  EXPECT_EQ(static_cast<uint32_t>(16), cmd.result_shm_offset);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, ColorMask) {
   cmds::ColorMask& cmd = *GetBufferAs<cmds::ColorMask>();
   void* next_cmd =
@@ -3423,6 +3441,20 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, WaitSync) {
+  cmds::WaitSync& cmd = *GetBufferAs<cmds::WaitSync>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLbitfield>(12),
+              static_cast<GLuint>(13), static_cast<GLuint>(14));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::WaitSync::kCmdId), cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.sync);
+  EXPECT_EQ(static_cast<GLbitfield>(12), cmd.flags);
+  EXPECT_EQ(static_cast<GLuint>(13), cmd.timeout_0);
+  EXPECT_EQ(static_cast<GLuint>(14), cmd.timeout_1);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, BlitFramebufferCHROMIUM) {
   cmds::BlitFramebufferCHROMIUM& cmd =
       *GetBufferAs<cmds::BlitFramebufferCHROMIUM>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index db67f4f..d652511 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -39,253 +39,255 @@
   OP(ClearColor)                               /* 280 */ \
   OP(ClearDepthf)                              /* 281 */ \
   OP(ClearStencil)                             /* 282 */ \
-  OP(ColorMask)                                /* 283 */ \
-  OP(CompileShader)                            /* 284 */ \
-  OP(CompressedTexImage2DBucket)               /* 285 */ \
-  OP(CompressedTexImage2D)                     /* 286 */ \
-  OP(CompressedTexSubImage2DBucket)            /* 287 */ \
-  OP(CompressedTexSubImage2D)                  /* 288 */ \
-  OP(CopyBufferSubData)                        /* 289 */ \
-  OP(CopyTexImage2D)                           /* 290 */ \
-  OP(CopyTexSubImage2D)                        /* 291 */ \
-  OP(CopyTexSubImage3D)                        /* 292 */ \
-  OP(CreateProgram)                            /* 293 */ \
-  OP(CreateShader)                             /* 294 */ \
-  OP(CullFace)                                 /* 295 */ \
-  OP(DeleteBuffersImmediate)                   /* 296 */ \
-  OP(DeleteFramebuffersImmediate)              /* 297 */ \
-  OP(DeleteProgram)                            /* 298 */ \
-  OP(DeleteRenderbuffersImmediate)             /* 299 */ \
-  OP(DeleteSamplersImmediate)                  /* 300 */ \
-  OP(DeleteSync)                               /* 301 */ \
-  OP(DeleteShader)                             /* 302 */ \
-  OP(DeleteTexturesImmediate)                  /* 303 */ \
-  OP(DeleteTransformFeedbacksImmediate)        /* 304 */ \
-  OP(DepthFunc)                                /* 305 */ \
-  OP(DepthMask)                                /* 306 */ \
-  OP(DepthRangef)                              /* 307 */ \
-  OP(DetachShader)                             /* 308 */ \
-  OP(Disable)                                  /* 309 */ \
-  OP(DisableVertexAttribArray)                 /* 310 */ \
-  OP(DrawArrays)                               /* 311 */ \
-  OP(DrawElements)                             /* 312 */ \
-  OP(Enable)                                   /* 313 */ \
-  OP(EnableVertexAttribArray)                  /* 314 */ \
-  OP(FenceSync)                                /* 315 */ \
-  OP(Finish)                                   /* 316 */ \
-  OP(Flush)                                    /* 317 */ \
-  OP(FramebufferRenderbuffer)                  /* 318 */ \
-  OP(FramebufferTexture2D)                     /* 319 */ \
-  OP(FramebufferTextureLayer)                  /* 320 */ \
-  OP(FrontFace)                                /* 321 */ \
-  OP(GenBuffersImmediate)                      /* 322 */ \
-  OP(GenerateMipmap)                           /* 323 */ \
-  OP(GenFramebuffersImmediate)                 /* 324 */ \
-  OP(GenRenderbuffersImmediate)                /* 325 */ \
-  OP(GenSamplersImmediate)                     /* 326 */ \
-  OP(GenTexturesImmediate)                     /* 327 */ \
-  OP(GenTransformFeedbacksImmediate)           /* 328 */ \
-  OP(GetActiveAttrib)                          /* 329 */ \
-  OP(GetActiveUniform)                         /* 330 */ \
-  OP(GetActiveUniformBlockiv)                  /* 331 */ \
-  OP(GetActiveUniformBlockName)                /* 332 */ \
-  OP(GetActiveUniformsiv)                      /* 333 */ \
-  OP(GetAttachedShaders)                       /* 334 */ \
-  OP(GetAttribLocation)                        /* 335 */ \
-  OP(GetBooleanv)                              /* 336 */ \
-  OP(GetBufferParameteriv)                     /* 337 */ \
-  OP(GetError)                                 /* 338 */ \
-  OP(GetFloatv)                                /* 339 */ \
-  OP(GetFragDataLocation)                      /* 340 */ \
-  OP(GetFramebufferAttachmentParameteriv)      /* 341 */ \
-  OP(GetIntegerv)                              /* 342 */ \
-  OP(GetInternalformativ)                      /* 343 */ \
-  OP(GetProgramiv)                             /* 344 */ \
-  OP(GetProgramInfoLog)                        /* 345 */ \
-  OP(GetRenderbufferParameteriv)               /* 346 */ \
-  OP(GetSamplerParameterfv)                    /* 347 */ \
-  OP(GetSamplerParameteriv)                    /* 348 */ \
-  OP(GetShaderiv)                              /* 349 */ \
-  OP(GetShaderInfoLog)                         /* 350 */ \
-  OP(GetShaderPrecisionFormat)                 /* 351 */ \
-  OP(GetShaderSource)                          /* 352 */ \
-  OP(GetString)                                /* 353 */ \
-  OP(GetTexParameterfv)                        /* 354 */ \
-  OP(GetTexParameteriv)                        /* 355 */ \
-  OP(GetTransformFeedbackVarying)              /* 356 */ \
-  OP(GetUniformBlockIndex)                     /* 357 */ \
-  OP(GetUniformfv)                             /* 358 */ \
-  OP(GetUniformiv)                             /* 359 */ \
-  OP(GetUniformIndices)                        /* 360 */ \
-  OP(GetUniformLocation)                       /* 361 */ \
-  OP(GetVertexAttribfv)                        /* 362 */ \
-  OP(GetVertexAttribiv)                        /* 363 */ \
-  OP(GetVertexAttribPointerv)                  /* 364 */ \
-  OP(Hint)                                     /* 365 */ \
-  OP(InvalidateFramebufferImmediate)           /* 366 */ \
-  OP(InvalidateSubFramebufferImmediate)        /* 367 */ \
-  OP(IsBuffer)                                 /* 368 */ \
-  OP(IsEnabled)                                /* 369 */ \
-  OP(IsFramebuffer)                            /* 370 */ \
-  OP(IsProgram)                                /* 371 */ \
-  OP(IsRenderbuffer)                           /* 372 */ \
-  OP(IsSampler)                                /* 373 */ \
-  OP(IsShader)                                 /* 374 */ \
-  OP(IsSync)                                   /* 375 */ \
-  OP(IsTexture)                                /* 376 */ \
-  OP(IsTransformFeedback)                      /* 377 */ \
-  OP(LineWidth)                                /* 378 */ \
-  OP(LinkProgram)                              /* 379 */ \
-  OP(PauseTransformFeedback)                   /* 380 */ \
-  OP(PixelStorei)                              /* 381 */ \
-  OP(PolygonOffset)                            /* 382 */ \
-  OP(ReadBuffer)                               /* 383 */ \
-  OP(ReadPixels)                               /* 384 */ \
-  OP(ReleaseShaderCompiler)                    /* 385 */ \
-  OP(RenderbufferStorage)                      /* 386 */ \
-  OP(ResumeTransformFeedback)                  /* 387 */ \
-  OP(SampleCoverage)                           /* 388 */ \
-  OP(SamplerParameterf)                        /* 389 */ \
-  OP(SamplerParameterfvImmediate)              /* 390 */ \
-  OP(SamplerParameteri)                        /* 391 */ \
-  OP(SamplerParameterivImmediate)              /* 392 */ \
-  OP(Scissor)                                  /* 393 */ \
-  OP(ShaderBinary)                             /* 394 */ \
-  OP(ShaderSourceBucket)                       /* 395 */ \
-  OP(StencilFunc)                              /* 396 */ \
-  OP(StencilFuncSeparate)                      /* 397 */ \
-  OP(StencilMask)                              /* 398 */ \
-  OP(StencilMaskSeparate)                      /* 399 */ \
-  OP(StencilOp)                                /* 400 */ \
-  OP(StencilOpSeparate)                        /* 401 */ \
-  OP(TexImage2D)                               /* 402 */ \
-  OP(TexImage3D)                               /* 403 */ \
-  OP(TexParameterf)                            /* 404 */ \
-  OP(TexParameterfvImmediate)                  /* 405 */ \
-  OP(TexParameteri)                            /* 406 */ \
-  OP(TexParameterivImmediate)                  /* 407 */ \
-  OP(TexStorage3D)                             /* 408 */ \
-  OP(TexSubImage2D)                            /* 409 */ \
-  OP(TexSubImage3D)                            /* 410 */ \
-  OP(TransformFeedbackVaryingsBucket)          /* 411 */ \
-  OP(Uniform1f)                                /* 412 */ \
-  OP(Uniform1fvImmediate)                      /* 413 */ \
-  OP(Uniform1i)                                /* 414 */ \
-  OP(Uniform1ivImmediate)                      /* 415 */ \
-  OP(Uniform1ui)                               /* 416 */ \
-  OP(Uniform1uivImmediate)                     /* 417 */ \
-  OP(Uniform2f)                                /* 418 */ \
-  OP(Uniform2fvImmediate)                      /* 419 */ \
-  OP(Uniform2i)                                /* 420 */ \
-  OP(Uniform2ivImmediate)                      /* 421 */ \
-  OP(Uniform2ui)                               /* 422 */ \
-  OP(Uniform2uivImmediate)                     /* 423 */ \
-  OP(Uniform3f)                                /* 424 */ \
-  OP(Uniform3fvImmediate)                      /* 425 */ \
-  OP(Uniform3i)                                /* 426 */ \
-  OP(Uniform3ivImmediate)                      /* 427 */ \
-  OP(Uniform3ui)                               /* 428 */ \
-  OP(Uniform3uivImmediate)                     /* 429 */ \
-  OP(Uniform4f)                                /* 430 */ \
-  OP(Uniform4fvImmediate)                      /* 431 */ \
-  OP(Uniform4i)                                /* 432 */ \
-  OP(Uniform4ivImmediate)                      /* 433 */ \
-  OP(Uniform4ui)                               /* 434 */ \
-  OP(Uniform4uivImmediate)                     /* 435 */ \
-  OP(UniformBlockBinding)                      /* 436 */ \
-  OP(UniformMatrix2fvImmediate)                /* 437 */ \
-  OP(UniformMatrix2x3fvImmediate)              /* 438 */ \
-  OP(UniformMatrix2x4fvImmediate)              /* 439 */ \
-  OP(UniformMatrix3fvImmediate)                /* 440 */ \
-  OP(UniformMatrix3x2fvImmediate)              /* 441 */ \
-  OP(UniformMatrix3x4fvImmediate)              /* 442 */ \
-  OP(UniformMatrix4fvImmediate)                /* 443 */ \
-  OP(UniformMatrix4x2fvImmediate)              /* 444 */ \
-  OP(UniformMatrix4x3fvImmediate)              /* 445 */ \
-  OP(UseProgram)                               /* 446 */ \
-  OP(ValidateProgram)                          /* 447 */ \
-  OP(VertexAttrib1f)                           /* 448 */ \
-  OP(VertexAttrib1fvImmediate)                 /* 449 */ \
-  OP(VertexAttrib2f)                           /* 450 */ \
-  OP(VertexAttrib2fvImmediate)                 /* 451 */ \
-  OP(VertexAttrib3f)                           /* 452 */ \
-  OP(VertexAttrib3fvImmediate)                 /* 453 */ \
-  OP(VertexAttrib4f)                           /* 454 */ \
-  OP(VertexAttrib4fvImmediate)                 /* 455 */ \
-  OP(VertexAttribI4i)                          /* 456 */ \
-  OP(VertexAttribI4ivImmediate)                /* 457 */ \
-  OP(VertexAttribI4ui)                         /* 458 */ \
-  OP(VertexAttribI4uivImmediate)               /* 459 */ \
-  OP(VertexAttribIPointer)                     /* 460 */ \
-  OP(VertexAttribPointer)                      /* 461 */ \
-  OP(Viewport)                                 /* 462 */ \
-  OP(BlitFramebufferCHROMIUM)                  /* 463 */ \
-  OP(RenderbufferStorageMultisampleCHROMIUM)   /* 464 */ \
-  OP(RenderbufferStorageMultisampleEXT)        /* 465 */ \
-  OP(FramebufferTexture2DMultisampleEXT)       /* 466 */ \
-  OP(TexStorage2DEXT)                          /* 467 */ \
-  OP(GenQueriesEXTImmediate)                   /* 468 */ \
-  OP(DeleteQueriesEXTImmediate)                /* 469 */ \
-  OP(BeginQueryEXT)                            /* 470 */ \
-  OP(BeginTransformFeedback)                   /* 471 */ \
-  OP(EndQueryEXT)                              /* 472 */ \
-  OP(EndTransformFeedback)                     /* 473 */ \
-  OP(InsertEventMarkerEXT)                     /* 474 */ \
-  OP(PushGroupMarkerEXT)                       /* 475 */ \
-  OP(PopGroupMarkerEXT)                        /* 476 */ \
-  OP(GenVertexArraysOESImmediate)              /* 477 */ \
-  OP(DeleteVertexArraysOESImmediate)           /* 478 */ \
-  OP(IsVertexArrayOES)                         /* 479 */ \
-  OP(BindVertexArrayOES)                       /* 480 */ \
-  OP(SwapBuffers)                              /* 481 */ \
-  OP(GetMaxValueInBufferCHROMIUM)              /* 482 */ \
-  OP(EnableFeatureCHROMIUM)                    /* 483 */ \
-  OP(ResizeCHROMIUM)                           /* 484 */ \
-  OP(GetRequestableExtensionsCHROMIUM)         /* 485 */ \
-  OP(RequestExtensionCHROMIUM)                 /* 486 */ \
-  OP(GetProgramInfoCHROMIUM)                   /* 487 */ \
-  OP(GetUniformBlocksCHROMIUM)                 /* 488 */ \
-  OP(GetTransformFeedbackVaryingsCHROMIUM)     /* 489 */ \
-  OP(GetUniformsES3CHROMIUM)                   /* 490 */ \
-  OP(GetTranslatedShaderSourceANGLE)           /* 491 */ \
-  OP(PostSubBufferCHROMIUM)                    /* 492 */ \
-  OP(TexImageIOSurface2DCHROMIUM)              /* 493 */ \
-  OP(CopyTextureCHROMIUM)                      /* 494 */ \
-  OP(DrawArraysInstancedANGLE)                 /* 495 */ \
-  OP(DrawElementsInstancedANGLE)               /* 496 */ \
-  OP(VertexAttribDivisorANGLE)                 /* 497 */ \
-  OP(GenMailboxCHROMIUM)                       /* 498 */ \
-  OP(ProduceTextureCHROMIUMImmediate)          /* 499 */ \
-  OP(ProduceTextureDirectCHROMIUMImmediate)    /* 500 */ \
-  OP(ConsumeTextureCHROMIUMImmediate)          /* 501 */ \
-  OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 502 */ \
-  OP(BindUniformLocationCHROMIUMBucket)        /* 503 */ \
-  OP(GenValuebuffersCHROMIUMImmediate)         /* 504 */ \
-  OP(DeleteValuebuffersCHROMIUMImmediate)      /* 505 */ \
-  OP(IsValuebufferCHROMIUM)                    /* 506 */ \
-  OP(BindValuebufferCHROMIUM)                  /* 507 */ \
-  OP(SubscribeValueCHROMIUM)                   /* 508 */ \
-  OP(PopulateSubscribedValuesCHROMIUM)         /* 509 */ \
-  OP(UniformValuebufferCHROMIUM)               /* 510 */ \
-  OP(BindTexImage2DCHROMIUM)                   /* 511 */ \
-  OP(ReleaseTexImage2DCHROMIUM)                /* 512 */ \
-  OP(TraceBeginCHROMIUM)                       /* 513 */ \
-  OP(TraceEndCHROMIUM)                         /* 514 */ \
-  OP(AsyncTexSubImage2DCHROMIUM)               /* 515 */ \
-  OP(AsyncTexImage2DCHROMIUM)                  /* 516 */ \
-  OP(WaitAsyncTexImage2DCHROMIUM)              /* 517 */ \
-  OP(WaitAllAsyncTexImage2DCHROMIUM)           /* 518 */ \
-  OP(DiscardFramebufferEXTImmediate)           /* 519 */ \
-  OP(LoseContextCHROMIUM)                      /* 520 */ \
-  OP(InsertSyncPointCHROMIUM)                  /* 521 */ \
-  OP(WaitSyncPointCHROMIUM)                    /* 522 */ \
-  OP(DrawBuffersEXTImmediate)                  /* 523 */ \
-  OP(DiscardBackbufferCHROMIUM)                /* 524 */ \
-  OP(ScheduleOverlayPlaneCHROMIUM)             /* 525 */ \
-  OP(SwapInterval)                             /* 526 */ \
-  OP(MatrixLoadfCHROMIUMImmediate)             /* 527 */ \
-  OP(MatrixLoadIdentityCHROMIUM)               /* 528 */ \
-  OP(BlendBarrierKHR)                          /* 529 */
+  OP(ClientWaitSync)                           /* 283 */ \
+  OP(ColorMask)                                /* 284 */ \
+  OP(CompileShader)                            /* 285 */ \
+  OP(CompressedTexImage2DBucket)               /* 286 */ \
+  OP(CompressedTexImage2D)                     /* 287 */ \
+  OP(CompressedTexSubImage2DBucket)            /* 288 */ \
+  OP(CompressedTexSubImage2D)                  /* 289 */ \
+  OP(CopyBufferSubData)                        /* 290 */ \
+  OP(CopyTexImage2D)                           /* 291 */ \
+  OP(CopyTexSubImage2D)                        /* 292 */ \
+  OP(CopyTexSubImage3D)                        /* 293 */ \
+  OP(CreateProgram)                            /* 294 */ \
+  OP(CreateShader)                             /* 295 */ \
+  OP(CullFace)                                 /* 296 */ \
+  OP(DeleteBuffersImmediate)                   /* 297 */ \
+  OP(DeleteFramebuffersImmediate)              /* 298 */ \
+  OP(DeleteProgram)                            /* 299 */ \
+  OP(DeleteRenderbuffersImmediate)             /* 300 */ \
+  OP(DeleteSamplersImmediate)                  /* 301 */ \
+  OP(DeleteSync)                               /* 302 */ \
+  OP(DeleteShader)                             /* 303 */ \
+  OP(DeleteTexturesImmediate)                  /* 304 */ \
+  OP(DeleteTransformFeedbacksImmediate)        /* 305 */ \
+  OP(DepthFunc)                                /* 306 */ \
+  OP(DepthMask)                                /* 307 */ \
+  OP(DepthRangef)                              /* 308 */ \
+  OP(DetachShader)                             /* 309 */ \
+  OP(Disable)                                  /* 310 */ \
+  OP(DisableVertexAttribArray)                 /* 311 */ \
+  OP(DrawArrays)                               /* 312 */ \
+  OP(DrawElements)                             /* 313 */ \
+  OP(Enable)                                   /* 314 */ \
+  OP(EnableVertexAttribArray)                  /* 315 */ \
+  OP(FenceSync)                                /* 316 */ \
+  OP(Finish)                                   /* 317 */ \
+  OP(Flush)                                    /* 318 */ \
+  OP(FramebufferRenderbuffer)                  /* 319 */ \
+  OP(FramebufferTexture2D)                     /* 320 */ \
+  OP(FramebufferTextureLayer)                  /* 321 */ \
+  OP(FrontFace)                                /* 322 */ \
+  OP(GenBuffersImmediate)                      /* 323 */ \
+  OP(GenerateMipmap)                           /* 324 */ \
+  OP(GenFramebuffersImmediate)                 /* 325 */ \
+  OP(GenRenderbuffersImmediate)                /* 326 */ \
+  OP(GenSamplersImmediate)                     /* 327 */ \
+  OP(GenTexturesImmediate)                     /* 328 */ \
+  OP(GenTransformFeedbacksImmediate)           /* 329 */ \
+  OP(GetActiveAttrib)                          /* 330 */ \
+  OP(GetActiveUniform)                         /* 331 */ \
+  OP(GetActiveUniformBlockiv)                  /* 332 */ \
+  OP(GetActiveUniformBlockName)                /* 333 */ \
+  OP(GetActiveUniformsiv)                      /* 334 */ \
+  OP(GetAttachedShaders)                       /* 335 */ \
+  OP(GetAttribLocation)                        /* 336 */ \
+  OP(GetBooleanv)                              /* 337 */ \
+  OP(GetBufferParameteriv)                     /* 338 */ \
+  OP(GetError)                                 /* 339 */ \
+  OP(GetFloatv)                                /* 340 */ \
+  OP(GetFragDataLocation)                      /* 341 */ \
+  OP(GetFramebufferAttachmentParameteriv)      /* 342 */ \
+  OP(GetIntegerv)                              /* 343 */ \
+  OP(GetInternalformativ)                      /* 344 */ \
+  OP(GetProgramiv)                             /* 345 */ \
+  OP(GetProgramInfoLog)                        /* 346 */ \
+  OP(GetRenderbufferParameteriv)               /* 347 */ \
+  OP(GetSamplerParameterfv)                    /* 348 */ \
+  OP(GetSamplerParameteriv)                    /* 349 */ \
+  OP(GetShaderiv)                              /* 350 */ \
+  OP(GetShaderInfoLog)                         /* 351 */ \
+  OP(GetShaderPrecisionFormat)                 /* 352 */ \
+  OP(GetShaderSource)                          /* 353 */ \
+  OP(GetString)                                /* 354 */ \
+  OP(GetTexParameterfv)                        /* 355 */ \
+  OP(GetTexParameteriv)                        /* 356 */ \
+  OP(GetTransformFeedbackVarying)              /* 357 */ \
+  OP(GetUniformBlockIndex)                     /* 358 */ \
+  OP(GetUniformfv)                             /* 359 */ \
+  OP(GetUniformiv)                             /* 360 */ \
+  OP(GetUniformIndices)                        /* 361 */ \
+  OP(GetUniformLocation)                       /* 362 */ \
+  OP(GetVertexAttribfv)                        /* 363 */ \
+  OP(GetVertexAttribiv)                        /* 364 */ \
+  OP(GetVertexAttribPointerv)                  /* 365 */ \
+  OP(Hint)                                     /* 366 */ \
+  OP(InvalidateFramebufferImmediate)           /* 367 */ \
+  OP(InvalidateSubFramebufferImmediate)        /* 368 */ \
+  OP(IsBuffer)                                 /* 369 */ \
+  OP(IsEnabled)                                /* 370 */ \
+  OP(IsFramebuffer)                            /* 371 */ \
+  OP(IsProgram)                                /* 372 */ \
+  OP(IsRenderbuffer)                           /* 373 */ \
+  OP(IsSampler)                                /* 374 */ \
+  OP(IsShader)                                 /* 375 */ \
+  OP(IsSync)                                   /* 376 */ \
+  OP(IsTexture)                                /* 377 */ \
+  OP(IsTransformFeedback)                      /* 378 */ \
+  OP(LineWidth)                                /* 379 */ \
+  OP(LinkProgram)                              /* 380 */ \
+  OP(PauseTransformFeedback)                   /* 381 */ \
+  OP(PixelStorei)                              /* 382 */ \
+  OP(PolygonOffset)                            /* 383 */ \
+  OP(ReadBuffer)                               /* 384 */ \
+  OP(ReadPixels)                               /* 385 */ \
+  OP(ReleaseShaderCompiler)                    /* 386 */ \
+  OP(RenderbufferStorage)                      /* 387 */ \
+  OP(ResumeTransformFeedback)                  /* 388 */ \
+  OP(SampleCoverage)                           /* 389 */ \
+  OP(SamplerParameterf)                        /* 390 */ \
+  OP(SamplerParameterfvImmediate)              /* 391 */ \
+  OP(SamplerParameteri)                        /* 392 */ \
+  OP(SamplerParameterivImmediate)              /* 393 */ \
+  OP(Scissor)                                  /* 394 */ \
+  OP(ShaderBinary)                             /* 395 */ \
+  OP(ShaderSourceBucket)                       /* 396 */ \
+  OP(StencilFunc)                              /* 397 */ \
+  OP(StencilFuncSeparate)                      /* 398 */ \
+  OP(StencilMask)                              /* 399 */ \
+  OP(StencilMaskSeparate)                      /* 400 */ \
+  OP(StencilOp)                                /* 401 */ \
+  OP(StencilOpSeparate)                        /* 402 */ \
+  OP(TexImage2D)                               /* 403 */ \
+  OP(TexImage3D)                               /* 404 */ \
+  OP(TexParameterf)                            /* 405 */ \
+  OP(TexParameterfvImmediate)                  /* 406 */ \
+  OP(TexParameteri)                            /* 407 */ \
+  OP(TexParameterivImmediate)                  /* 408 */ \
+  OP(TexStorage3D)                             /* 409 */ \
+  OP(TexSubImage2D)                            /* 410 */ \
+  OP(TexSubImage3D)                            /* 411 */ \
+  OP(TransformFeedbackVaryingsBucket)          /* 412 */ \
+  OP(Uniform1f)                                /* 413 */ \
+  OP(Uniform1fvImmediate)                      /* 414 */ \
+  OP(Uniform1i)                                /* 415 */ \
+  OP(Uniform1ivImmediate)                      /* 416 */ \
+  OP(Uniform1ui)                               /* 417 */ \
+  OP(Uniform1uivImmediate)                     /* 418 */ \
+  OP(Uniform2f)                                /* 419 */ \
+  OP(Uniform2fvImmediate)                      /* 420 */ \
+  OP(Uniform2i)                                /* 421 */ \
+  OP(Uniform2ivImmediate)                      /* 422 */ \
+  OP(Uniform2ui)                               /* 423 */ \
+  OP(Uniform2uivImmediate)                     /* 424 */ \
+  OP(Uniform3f)                                /* 425 */ \
+  OP(Uniform3fvImmediate)                      /* 426 */ \
+  OP(Uniform3i)                                /* 427 */ \
+  OP(Uniform3ivImmediate)                      /* 428 */ \
+  OP(Uniform3ui)                               /* 429 */ \
+  OP(Uniform3uivImmediate)                     /* 430 */ \
+  OP(Uniform4f)                                /* 431 */ \
+  OP(Uniform4fvImmediate)                      /* 432 */ \
+  OP(Uniform4i)                                /* 433 */ \
+  OP(Uniform4ivImmediate)                      /* 434 */ \
+  OP(Uniform4ui)                               /* 435 */ \
+  OP(Uniform4uivImmediate)                     /* 436 */ \
+  OP(UniformBlockBinding)                      /* 437 */ \
+  OP(UniformMatrix2fvImmediate)                /* 438 */ \
+  OP(UniformMatrix2x3fvImmediate)              /* 439 */ \
+  OP(UniformMatrix2x4fvImmediate)              /* 440 */ \
+  OP(UniformMatrix3fvImmediate)                /* 441 */ \
+  OP(UniformMatrix3x2fvImmediate)              /* 442 */ \
+  OP(UniformMatrix3x4fvImmediate)              /* 443 */ \
+  OP(UniformMatrix4fvImmediate)                /* 444 */ \
+  OP(UniformMatrix4x2fvImmediate)              /* 445 */ \
+  OP(UniformMatrix4x3fvImmediate)              /* 446 */ \
+  OP(UseProgram)                               /* 447 */ \
+  OP(ValidateProgram)                          /* 448 */ \
+  OP(VertexAttrib1f)                           /* 449 */ \
+  OP(VertexAttrib1fvImmediate)                 /* 450 */ \
+  OP(VertexAttrib2f)                           /* 451 */ \
+  OP(VertexAttrib2fvImmediate)                 /* 452 */ \
+  OP(VertexAttrib3f)                           /* 453 */ \
+  OP(VertexAttrib3fvImmediate)                 /* 454 */ \
+  OP(VertexAttrib4f)                           /* 455 */ \
+  OP(VertexAttrib4fvImmediate)                 /* 456 */ \
+  OP(VertexAttribI4i)                          /* 457 */ \
+  OP(VertexAttribI4ivImmediate)                /* 458 */ \
+  OP(VertexAttribI4ui)                         /* 459 */ \
+  OP(VertexAttribI4uivImmediate)               /* 460 */ \
+  OP(VertexAttribIPointer)                     /* 461 */ \
+  OP(VertexAttribPointer)                      /* 462 */ \
+  OP(Viewport)                                 /* 463 */ \
+  OP(WaitSync)                                 /* 464 */ \
+  OP(BlitFramebufferCHROMIUM)                  /* 465 */ \
+  OP(RenderbufferStorageMultisampleCHROMIUM)   /* 466 */ \
+  OP(RenderbufferStorageMultisampleEXT)        /* 467 */ \
+  OP(FramebufferTexture2DMultisampleEXT)       /* 468 */ \
+  OP(TexStorage2DEXT)                          /* 469 */ \
+  OP(GenQueriesEXTImmediate)                   /* 470 */ \
+  OP(DeleteQueriesEXTImmediate)                /* 471 */ \
+  OP(BeginQueryEXT)                            /* 472 */ \
+  OP(BeginTransformFeedback)                   /* 473 */ \
+  OP(EndQueryEXT)                              /* 474 */ \
+  OP(EndTransformFeedback)                     /* 475 */ \
+  OP(InsertEventMarkerEXT)                     /* 476 */ \
+  OP(PushGroupMarkerEXT)                       /* 477 */ \
+  OP(PopGroupMarkerEXT)                        /* 478 */ \
+  OP(GenVertexArraysOESImmediate)              /* 479 */ \
+  OP(DeleteVertexArraysOESImmediate)           /* 480 */ \
+  OP(IsVertexArrayOES)                         /* 481 */ \
+  OP(BindVertexArrayOES)                       /* 482 */ \
+  OP(SwapBuffers)                              /* 483 */ \
+  OP(GetMaxValueInBufferCHROMIUM)              /* 484 */ \
+  OP(EnableFeatureCHROMIUM)                    /* 485 */ \
+  OP(ResizeCHROMIUM)                           /* 486 */ \
+  OP(GetRequestableExtensionsCHROMIUM)         /* 487 */ \
+  OP(RequestExtensionCHROMIUM)                 /* 488 */ \
+  OP(GetProgramInfoCHROMIUM)                   /* 489 */ \
+  OP(GetUniformBlocksCHROMIUM)                 /* 490 */ \
+  OP(GetTransformFeedbackVaryingsCHROMIUM)     /* 491 */ \
+  OP(GetUniformsES3CHROMIUM)                   /* 492 */ \
+  OP(GetTranslatedShaderSourceANGLE)           /* 493 */ \
+  OP(PostSubBufferCHROMIUM)                    /* 494 */ \
+  OP(TexImageIOSurface2DCHROMIUM)              /* 495 */ \
+  OP(CopyTextureCHROMIUM)                      /* 496 */ \
+  OP(DrawArraysInstancedANGLE)                 /* 497 */ \
+  OP(DrawElementsInstancedANGLE)               /* 498 */ \
+  OP(VertexAttribDivisorANGLE)                 /* 499 */ \
+  OP(GenMailboxCHROMIUM)                       /* 500 */ \
+  OP(ProduceTextureCHROMIUMImmediate)          /* 501 */ \
+  OP(ProduceTextureDirectCHROMIUMImmediate)    /* 502 */ \
+  OP(ConsumeTextureCHROMIUMImmediate)          /* 503 */ \
+  OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 504 */ \
+  OP(BindUniformLocationCHROMIUMBucket)        /* 505 */ \
+  OP(GenValuebuffersCHROMIUMImmediate)         /* 506 */ \
+  OP(DeleteValuebuffersCHROMIUMImmediate)      /* 507 */ \
+  OP(IsValuebufferCHROMIUM)                    /* 508 */ \
+  OP(BindValuebufferCHROMIUM)                  /* 509 */ \
+  OP(SubscribeValueCHROMIUM)                   /* 510 */ \
+  OP(PopulateSubscribedValuesCHROMIUM)         /* 511 */ \
+  OP(UniformValuebufferCHROMIUM)               /* 512 */ \
+  OP(BindTexImage2DCHROMIUM)                   /* 513 */ \
+  OP(ReleaseTexImage2DCHROMIUM)                /* 514 */ \
+  OP(TraceBeginCHROMIUM)                       /* 515 */ \
+  OP(TraceEndCHROMIUM)                         /* 516 */ \
+  OP(AsyncTexSubImage2DCHROMIUM)               /* 517 */ \
+  OP(AsyncTexImage2DCHROMIUM)                  /* 518 */ \
+  OP(WaitAsyncTexImage2DCHROMIUM)              /* 519 */ \
+  OP(WaitAllAsyncTexImage2DCHROMIUM)           /* 520 */ \
+  OP(DiscardFramebufferEXTImmediate)           /* 521 */ \
+  OP(LoseContextCHROMIUM)                      /* 522 */ \
+  OP(InsertSyncPointCHROMIUM)                  /* 523 */ \
+  OP(WaitSyncPointCHROMIUM)                    /* 524 */ \
+  OP(DrawBuffersEXTImmediate)                  /* 525 */ \
+  OP(DiscardBackbufferCHROMIUM)                /* 526 */ \
+  OP(ScheduleOverlayPlaneCHROMIUM)             /* 527 */ \
+  OP(SwapInterval)                             /* 528 */ \
+  OP(MatrixLoadfCHROMIUMImmediate)             /* 529 */ \
+  OP(MatrixLoadIdentityCHROMIUM)               /* 530 */ \
+  OP(BlendBarrierKHR)                          /* 531 */
 
 enum CommandId {
   kStartPoint = cmd::kLastCommonId,  // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index 638245b..c16a962 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -846,6 +846,20 @@
   }
 }
 
+// static
+void GLES2Util::MapUint64ToTwoUint32(
+    uint64_t v64, uint32_t* v32_0, uint32_t* v32_1) {
+  DCHECK(v32_0 && v32_1);
+  *v32_0 = static_cast<uint32_t>(v64 & 0xFFFFFFFF);
+  *v32_1 = static_cast<uint32_t>((v64 & 0xFFFFFFFF00000000) >> 32);
+}
+
+// static
+uint64_t GLES2Util::MapTwoUint32ToUint64(uint32_t v32_0, uint32_t v32_1) {
+  uint64_t v64 = v32_1;
+  return (v64 << 32) | v32_0;
+}
+
 namespace {
 
 // WebGraphicsContext3DCommandBufferImpl configuration attributes. Those in
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h
index 127c2be..3528cfd 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -187,6 +187,10 @@
   static size_t CalcClearBufferivDataCount(int buffer);
   static size_t CalcClearBufferfvDataCount(int buffer);
 
+  static void MapUint64ToTwoUint32(
+      uint64_t v64, uint32_t* v32_0, uint32_t* v32_1);
+  static uint64_t MapTwoUint32ToUint64(uint32_t v32_0, uint32_t v32_1);
+
   #include "../common/gles2_cmd_utils_autogen.h"
 
  private:
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc
index d766abb..3e9e0b2 100644
--- a/gpu/command_buffer/service/framebuffer_manager.cc
+++ b/gpu/command_buffer/service/framebuffer_manager.cc
@@ -631,6 +631,14 @@
   }
 }
 
+void Framebuffer::DoUnbindGLAttachmentsForWorkaround(GLenum target) {
+  // Replace all attachments with the default Renderbuffer.
+  for (AttachmentMap::const_iterator it = attachments_.begin();
+       it != attachments_.end(); ++it) {
+    glFramebufferRenderbufferEXT(target, it->first, GL_RENDERBUFFER, 0);
+  }
+}
+
 void Framebuffer::AttachRenderbuffer(
     GLenum attachment, Renderbuffer* renderbuffer) {
   const Attachment* a = GetAttachment(attachment);
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index 78c11ad..75f7e2a 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -74,6 +74,11 @@
     GLenum attachment,
     bool cleared);
 
+  // Unbinds all attachments from this framebuffer for workaround
+  // 'unbind_attachments_on_bound_render_fbo_delete'.  The Framebuffer must be
+  // bound when calling this.
+  void DoUnbindGLAttachmentsForWorkaround(GLenum target);
+
   // Attaches a renderbuffer to a particlar attachment.
   // Pass null to detach.
   void AttachRenderbuffer(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 52a9c43..6b254e0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3099,11 +3099,16 @@
         GetFramebuffer(client_ids[ii]);
     if (framebuffer && !framebuffer->IsDeleted()) {
       if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) {
-        framebuffer_state_.bound_draw_framebuffer = NULL;
-        framebuffer_state_.clear_state_dirty = true;
         GLenum target = supports_separate_framebuffer_binds ?
             GL_DRAW_FRAMEBUFFER_EXT : GL_FRAMEBUFFER;
+
+        // Unbind attachments on FBO before deletion.
+        if (workarounds().unbind_attachments_on_bound_render_fbo_delete)
+          framebuffer->DoUnbindGLAttachmentsForWorkaround(target);
+
         glBindFramebufferEXT(target, GetBackbufferServiceId());
+        framebuffer_state_.bound_draw_framebuffer = NULL;
+        framebuffer_state_.clear_state_dirty = true;
       }
       if (framebuffer == framebuffer_state_.bound_read_framebuffer.get()) {
         framebuffer_state_.bound_read_framebuffer = NULL;
@@ -12055,6 +12060,51 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderImpl::HandleClientWaitSync(
+    uint32_t immediate_data_size, const void* cmd_data) {
+  if (!unsafe_es3_apis_enabled())
+    return error::kUnknownCommand;
+  const gles2::cmds::ClientWaitSync& c =
+      *static_cast<const gles2::cmds::ClientWaitSync*>(cmd_data);
+  GLuint sync = static_cast<GLuint>(c.sync);
+  GLbitfield flags = static_cast<GLbitfield>(c.flags);
+  GLuint64 timeout = GLES2Util::MapTwoUint32ToUint64(c.timeout_0, c.timeout_1);
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result_dst = GetSharedMemoryAs<Result*>(
+      c.result_shm_id, c.result_shm_offset, sizeof(*result_dst));
+  if (!result_dst) {
+    return error::kOutOfBounds;
+  }
+  if (*result_dst != GL_WAIT_FAILED) {
+    return error::kInvalidArguments;
+  }
+  GLsync service_sync = 0;
+  if (!group_->GetSyncServiceId(sync, &service_sync)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "ClientWaitSync", "invalid sync");
+    return error::kNoError;
+  }
+  *result_dst = glClientWaitSync(service_sync, flags, timeout);
+  return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleWaitSync(
+    uint32_t immediate_data_size, const void* cmd_data) {
+  if (!unsafe_es3_apis_enabled())
+    return error::kUnknownCommand;
+  const gles2::cmds::WaitSync& c =
+      *static_cast<const gles2::cmds::WaitSync*>(cmd_data);
+  GLuint sync = static_cast<GLuint>(c.sync);
+  GLbitfield flags = static_cast<GLbitfield>(c.flags);
+  GLuint64 timeout = GLES2Util::MapTwoUint32ToUint64(c.timeout_0, c.timeout_1);
+  GLsync service_sync = 0;
+  if (!group_->GetSyncServiceId(sync, &service_sync)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "WaitSync", "invalid sync");
+    return error::kNoError;
+  }
+  glWaitSync(service_sync, flags, timeout);
+  return error::kNoError;
+}
+
 void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer(
     TextureRef* texture_ref) {
   Texture* texture = texture_ref->texture();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 905a390..e07a8ee 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -265,6 +265,115 @@
   EXPECT_FALSE(DoIsTexture(client_texture_id_));
 }
 
+TEST_P(GLES2DecoderTest, ClientWaitSyncValid) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  EXPECT_CALL(*gl_,
+              ClientWaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
+                             GL_SYNC_FLUSH_COMMANDS_BIT, 0))
+      .WillOnce(Return(GL_CONDITION_SATISFIED))
+      .RetiresOnSaturation();
+  *result = GL_WAIT_FAILED;
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result);
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  decoder_->set_unsafe_es3_apis_enabled(false);
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncNonZeroTimeoutValid) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  const GLuint64 kTimeout = 0xABCDEF0123456789;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  EXPECT_CALL(*gl_,
+              ClientWaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
+                             GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout))
+      .WillOnce(Return(GL_CONDITION_SATISFIED))
+      .RetiresOnSaturation();
+  *result = GL_WAIT_FAILED;
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result);
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  decoder_->set_unsafe_es3_apis_enabled(false);
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncInvalidSyncFails) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  cmd.Init(kInvalidClientId, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  *result = GL_WAIT_FAILED;
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(static_cast<GLenum>(GL_WAIT_FAILED), *result);
+  EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncResultNotInitFails) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  *result = 1;  // Any value other than GL_WAIT_FAILED
+  EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncBadSharedMemoryFails) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  *result = GL_WAIT_FAILED;
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           kInvalidSharedMemoryId, shared_memory_offset_);
+  EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+
+  *result = GL_WAIT_FAILED;
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, kInvalidSharedMemoryOffset);
+  EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, WaitSyncValidArgs) {
+  const GLuint64 kTimeout = GL_TIMEOUT_IGNORED;
+  EXPECT_CALL(*gl_, WaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
+                             0, kTimeout))
+      .Times(1)
+      .RetiresOnSaturation();
+
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  cmds::WaitSync cmd;
+  cmd.Init(client_sync_id_, 0, v32_0, v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  decoder_->set_unsafe_es3_apis_enabled(false);
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
+}
+
 TEST_P(GLES2DecoderManualInitTest, BindGeneratesResourceFalse) {
   InitState init;
   InitDecoder(init);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
index a255fe8..f03d3a5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
@@ -486,6 +486,7 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
+// TODO(gman): ClientWaitSync
 
 TEST_P(GLES2DecoderTest1, ColorMaskValidArgs) {
   SpecializedSetup<cmds::ColorMask, 0>(true);
@@ -1810,6 +1811,4 @@
 // TODO(gman): GetShaderPrecisionFormat
 
 // TODO(gman): GetShaderSource
-// TODO(gman): GetString
-
 #endif  // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_1_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
index 2e60219..447914c 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
@@ -12,6 +12,8 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
 
+// TODO(gman): GetString
+
 TEST_P(GLES2DecoderTest2, GetTexParameterfvValidArgs) {
   EXPECT_CALL(*gl_, GetError())
       .WillOnce(Return(GL_NO_ERROR))
@@ -1581,18 +1583,4 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
-
-TEST_P(GLES2DecoderTest2, VertexAttrib2fvImmediateValidArgs) {
-  cmds::VertexAttrib2fvImmediate& cmd =
-      *GetImmediateAs<cmds::VertexAttrib2fvImmediate>();
-  SpecializedSetup<cmds::VertexAttrib2fvImmediate, 0>(true);
-  GLfloat temp[2] = {
-      0,
-  };
-  cmd.Init(1, &temp[0]);
-  EXPECT_CALL(*gl_, VertexAttrib2fv(1, reinterpret_cast<GLfloat*>(
-                                           ImmediateDataAddress(&cmd))));
-  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
-}
 #endif  // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
index 7a58038..1add369 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
@@ -12,6 +12,20 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_
 
+TEST_P(GLES2DecoderTest3, VertexAttrib2fvImmediateValidArgs) {
+  cmds::VertexAttrib2fvImmediate& cmd =
+      *GetImmediateAs<cmds::VertexAttrib2fvImmediate>();
+  SpecializedSetup<cmds::VertexAttrib2fvImmediate, 0>(true);
+  GLfloat temp[2] = {
+      0,
+  };
+  cmd.Init(1, &temp[0]);
+  EXPECT_CALL(*gl_, VertexAttrib2fv(1, reinterpret_cast<GLfloat*>(
+                                           ImmediateDataAddress(&cmd))));
+  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
 TEST_P(GLES2DecoderTest3, VertexAttrib3fValidArgs) {
   EXPECT_CALL(*gl_, VertexAttrib3f(1, 2, 3, 4));
   SpecializedSetup<cmds::VertexAttrib3f, 0>(true);
@@ -145,6 +159,8 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
 }
+// TODO(gman): WaitSync
+
 // TODO(gman): TexStorage2DEXT
 // TODO(gman): GenQueriesEXTImmediate
 // TODO(gman): DeleteQueriesEXTImmediate
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
index 27c61ad..abf1e90 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
@@ -64,6 +64,7 @@
 ValueValidator<GLenum> stencil_op;
 ValueValidator<GLenum> string_type;
 ValueValidator<GLenum> subscription_target;
+ValueValidator<GLbitfield> sync_flush_flags;
 ValueValidator<GLenum> texture_3_d_target;
 ValueValidator<GLenum> texture_bind_target;
 ValueValidator<GLenum> texture_format;
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index 97c3cdd..68df541 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -457,6 +457,11 @@
     GL_MOUSE_POSITION_CHROMIUM,
 };
 
+static const GLbitfield valid_sync_flush_flags_table[] = {
+    GL_SYNC_FLUSH_COMMANDS_BIT,
+    0,
+};
+
 static const GLenum valid_texture_3_d_target_table[] = {
     GL_TEXTURE_3D,
     GL_TEXTURE_2D_ARRAY,
@@ -688,6 +693,8 @@
       string_type(valid_string_type_table, arraysize(valid_string_type_table)),
       subscription_target(valid_subscription_target_table,
                           arraysize(valid_subscription_target_table)),
+      sync_flush_flags(valid_sync_flush_flags_table,
+                       arraysize(valid_sync_flush_flags_table)),
       texture_3_d_target(valid_texture_3_d_target_table,
                          arraysize(valid_texture_3_d_target_table)),
       texture_bind_target(valid_texture_bind_target_table,
diff --git a/gpu/command_buffer/service/gpu_timing.cc b/gpu/command_buffer/service/gpu_timing.cc
index 8716448..6ab3e83 100644
--- a/gpu/command_buffer/service/gpu_timing.cc
+++ b/gpu/command_buffer/service/gpu_timing.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/gpu_timing.h"
 
 #include "base/time/time.h"
+#include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_version_info.h"
 
@@ -23,11 +24,11 @@
 void GPUTimer::Start() {
   // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
   glQueryCounter(queries_[0], GL_TIMESTAMP);
-  offset_ = gpu_timing_->CalculateTimerOffset();
 }
 
 void GPUTimer::End() {
   end_requested_ = true;
+  offset_ = gpu_timing_->CalculateTimerOffset();
   glQueryCounter(queries_[1], GL_TIMESTAMP);
 }
 
diff --git a/gpu/command_buffer/service/gpu_timing.h b/gpu/command_buffer/service/gpu_timing.h
index 1d9ecf6..726ac4e 100644
--- a/gpu/command_buffer/service/gpu_timing.h
+++ b/gpu/command_buffer/service/gpu_timing.h
@@ -8,10 +8,12 @@
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
 #include "gpu/gpu_export.h"
-#include "ui/gl/gl_bindings.h"
+
+namespace gfx {
+class GLContext;
+}
 
 namespace gpu {
-
 class GPUTiming;
 
 // Class to compute the amount of time it takes to fully
@@ -30,7 +32,7 @@
   int64 GetDeltaElapsed();
 
  private:
-  GLuint queries_[2];
+  unsigned int queries_[2];
   int64 offset_ = 0;
   bool end_requested_ = false;
   GPUTiming* gpu_timing_;
diff --git a/gpu/command_buffer/service/gpu_tracer.cc b/gpu/command_buffer/service/gpu_tracer.cc
index 6bedb7f..cc484dc 100644
--- a/gpu/command_buffer/service/gpu_tracer.cc
+++ b/gpu/command_buffer/service/gpu_tracer.cc
@@ -12,6 +12,7 @@
 #include "base/trace_event/trace_event.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/service/context_group.h"
+#include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_version_info.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/gpu_tracer.h b/gpu/command_buffer/service/gpu_tracer.h
index c8ac9d7..7aba612 100644
--- a/gpu/command_buffer/service/gpu_tracer.h
+++ b/gpu/command_buffer/service/gpu_tracer.h
@@ -17,7 +17,6 @@
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/gpu_timing.h"
 #include "gpu/gpu_export.h"
-#include "ui/gl/gl_bindings.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/service/gpu_tracer_unittest.cc b/gpu/command_buffer/service/gpu_tracer_unittest.cc
index 9005421..ba61ba3 100644
--- a/gpu/command_buffer/service/gpu_tracer_unittest.cc
+++ b/gpu/command_buffer/service/gpu_tracer_unittest.cc
@@ -356,6 +356,7 @@
 
     // Shouldn't be available before End() call
     gl_fake_queries_.SetCurrentGLTime(end_timestamp);
+    g_fakeCPUTime = expect_end_time;
     EXPECT_FALSE(trace->IsAvailable());
 
     trace->End(true);
@@ -474,7 +475,7 @@
       gl_fake_queries_.SetCurrentGLTime(
           end_timestamp +
           (i * base::Time::kNanosecondsPerMicrosecond));
-      g_fakeCPUTime = expect_start_time + i;
+      g_fakeCPUTime = expect_end_time + i;
 
       // Each trace name should be different to differentiate.
       const char num_char = static_cast<char>('0' + i);
diff --git a/gpu/command_buffer/service/mailbox_manager_sync.cc b/gpu/command_buffer/service/mailbox_manager_sync.cc
index 4f24bd7..4cdc80d 100644
--- a/gpu/command_buffer/service/mailbox_manager_sync.cc
+++ b/gpu/command_buffer/service/mailbox_manager_sync.cc
@@ -22,18 +22,6 @@
 
 namespace {
 
-bool SkipTextureWorkarounds(const Texture* texture) {
-  // TODO(sievers): crbug.com/352274
-  // Should probably only fail if it already *has* mipmaps, while allowing
-  // incomplete textures here.
-  bool needs_mips =
-      texture->min_filter() != GL_NEAREST && texture->min_filter() != GL_LINEAR;
-  if (texture->target() != GL_TEXTURE_2D || needs_mips || !texture->IsDefined())
-    return true;
-
-  return false;
-}
-
 base::LazyInstance<base::Lock> g_lock = LAZY_INSTANCE_INITIALIZER;
 
 typedef std::map<uint32, linked_ptr<gfx::GLFence>> SyncPointToFenceMap;
@@ -196,6 +184,15 @@
   DCHECK_EQ(0U, texture_to_group_.size());
 }
 
+// static
+bool MailboxManagerSync::SkipTextureWorkarounds(const Texture* texture) {
+  // Cannot support mips due to support mismatch between
+  // EGL_KHR_gl_texture_2D_image and glEGLImageTargetTexture2DOES for
+  // texture levels.
+  bool has_mips = texture->NeedsMips() && texture->texture_complete();
+  return texture->target() != GL_TEXTURE_2D || has_mips;
+}
+
 bool MailboxManagerSync::UsesSync() {
   return true;
 }
@@ -294,6 +291,7 @@
   if (definition.Matches(texture))
     return;
 
+  DCHECK_IMPLIES(gl_image, image_buffer.get());
   if (gl_image && !image_buffer->IsClient(gl_image)) {
     LOG(ERROR) << "MailboxSync: Incompatible attachment";
     return;
diff --git a/gpu/command_buffer/service/mailbox_manager_sync.h b/gpu/command_buffer/service/mailbox_manager_sync.h
index 4727f3b..4b3abf9 100644
--- a/gpu/command_buffer/service/mailbox_manager_sync.h
+++ b/gpu/command_buffer/service/mailbox_manager_sync.h
@@ -40,6 +40,8 @@
  private:
   friend class base::RefCounted<MailboxManager>;
 
+  static bool SkipTextureWorkarounds(const Texture* texture);
+
   ~MailboxManagerSync() override;
 
   class TextureGroup : public base::RefCounted<TextureGroup> {
diff --git a/gpu/command_buffer/service/mailbox_manager_unittest.cc b/gpu/command_buffer/service/mailbox_manager_unittest.cc
index 388e1da..a22078c 100644
--- a/gpu/command_buffer/service/mailbox_manager_unittest.cc
+++ b/gpu/command_buffer/service/mailbox_manager_unittest.cc
@@ -595,6 +595,66 @@
   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name));
 }
 
+TEST_F(MailboxManagerSyncTest, SyncIncompleteTexture) {
+  const GLuint kNewTextureId = 1234;
+
+  // Create but not define texture.
+  Texture* texture = CreateTexture();
+  SetTarget(texture, GL_TEXTURE_2D, 1);
+  EXPECT_FALSE(texture->IsDefined());
+
+  Mailbox name = Mailbox::Generate();
+  manager_->ProduceTexture(name, texture);
+  EXPECT_EQ(texture, manager_->ConsumeTexture(name));
+
+  // Synchronize
+  manager_->PushTextureUpdates(0);
+  manager2_->PullTextureUpdates(0);
+
+  // Should sync to new texture which is not defined.
+  EXPECT_CALL(*gl_, GenTextures(1, _))
+      .WillOnce(SetArgPointee<1>(kNewTextureId));
+  SetupUpdateTexParamExpectations(kNewTextureId, texture->min_filter(),
+                                  texture->mag_filter(), texture->wrap_s(),
+                                  texture->wrap_t());
+  Texture* new_texture = manager2_->ConsumeTexture(name);
+  ASSERT_TRUE(new_texture);
+  EXPECT_NE(texture, new_texture);
+  EXPECT_EQ(kNewTextureId, new_texture->service_id());
+  EXPECT_FALSE(new_texture->IsDefined());
+
+  // Change cleared to false.
+  SetLevelInfo(texture,
+               GL_TEXTURE_2D,
+               0,
+               GL_RGBA,
+               1,
+               1,
+               1,
+               0,
+               GL_RGBA,
+               GL_UNSIGNED_BYTE,
+               true);
+  SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  SetParameter(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  EXPECT_TRUE(texture->IsDefined());
+
+  // Synchronize
+  manager_->PushTextureUpdates(0);
+  SetupUpdateTexParamExpectations(
+      kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
+  manager2_->PullTextureUpdates(0);
+
+  // Cleared state should be synced.
+  EXPECT_TRUE(new_texture->IsDefined());
+
+  DestroyTexture(texture);
+  DestroyTexture(new_texture);
+
+  EXPECT_EQ(NULL, manager_->ConsumeTexture(name));
+  EXPECT_EQ(NULL, manager2_->ConsumeTexture(name));
+}
+
 // Putting the same texture into multiple mailboxes should result in sharing
 // only a single texture also within a synchronized manager instance.
 TEST_F(MailboxManagerSyncTest, SharedThroughMultipleMailboxes) {
@@ -671,8 +731,6 @@
   DestroyTexture(new_texture);
 }
 
-// TODO: Produce incomplete texture
-
 // TODO: Texture::level_infos_[][].size()
 
 // TODO: unsupported targets and formats
diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc
index 055cd70..d88a628 100644
--- a/gpu/command_buffer/service/shader_manager.cc
+++ b/gpu/command_buffer/service/shader_manager.cc
@@ -81,15 +81,18 @@
     glGetShaderiv(service_id_,
                   GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE,
                   &max_len);
+    source_for_driver = "\0";
     translated_source_.resize(max_len);
-    GLint len = 0;
-    glGetTranslatedShaderSourceANGLE(
-        service_id_, translated_source_.size(),
-        &len, &translated_source_.at(0));
-    DCHECK(max_len == 0 || len < max_len);
-    DCHECK(len == 0 || translated_source_[len] == '\0');
-    translated_source_.resize(len);
-    source_for_driver = translated_source_.c_str();
+    if (max_len) {
+      GLint len = 0;
+      glGetTranslatedShaderSourceANGLE(
+          service_id_, translated_source_.size(),
+          &len, &translated_source_.at(0));
+      DCHECK(max_len == 0 || len < max_len);
+      DCHECK(len == 0 || translated_source_[len] == '\0');
+      translated_source_.resize(len);
+      source_for_driver = translated_source_.c_str();
+    }
   }
 
   GLint status = GL_FALSE;
@@ -97,18 +100,21 @@
   if (status == GL_TRUE) {
     valid_ = true;
   } else {
+    valid_ = false;
+
     // We cannot reach here if we are using the shader translator.
     // All invalid shaders must be rejected by the translator.
     // All translated shaders must compile.
     GLint max_len = 0;
     glGetShaderiv(service_id_, GL_INFO_LOG_LENGTH, &max_len);
     log_info_.resize(max_len);
-    GLint len = 0;
-    glGetShaderInfoLog(service_id_, log_info_.size(), &len, &log_info_.at(0));
-    DCHECK(max_len == 0 || len < max_len);
-    DCHECK(len == 0 || log_info_[len] == '\0');
-    valid_ = false;
-    log_info_.resize(len);
+    if (max_len) {
+      GLint len = 0;
+      glGetShaderInfoLog(service_id_, log_info_.size(), &len, &log_info_.at(0));
+      DCHECK(max_len == 0 || len < max_len);
+      DCHECK(len == 0 || log_info_[len] == '\0');
+      log_info_.resize(len);
+    }
     LOG_IF(ERROR, translator)
         << "Shader translator allowed/produced an invalid shader "
         << "unless the driver is buggy:"
diff --git a/gpu/command_buffer/service/texture_definition.cc b/gpu/command_buffer/service/texture_definition.cc
index 7af662c..f2091b5 100644
--- a/gpu/command_buffer/service/texture_definition.cc
+++ b/gpu/command_buffer/service/texture_definition.cc
@@ -159,8 +159,11 @@
   EGLImageKHR egl_image = eglCreateImageKHR(
       egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list);
 
-  if (egl_image == EGL_NO_IMAGE_KHR)
+  if (egl_image == EGL_NO_IMAGE_KHR) {
+    LOG(ERROR) << "eglCreateImageKHR for cross-thread sharing failed: 0x"
+               << std::hex << eglGetError();
     return NULL;
+  }
 
   return new NativeImageBufferEGL(egl_display, egl_image);
 }
@@ -257,6 +260,18 @@
   }
 }
 
+TextureDefinition::LevelInfo::LevelInfo()
+    : target(0),
+      internal_format(0),
+      width(0),
+      height(0),
+      depth(0),
+      border(0),
+      format(0),
+      type(0),
+      cleared(false) {
+}
+
 TextureDefinition::LevelInfo::LevelInfo(GLenum target,
                                         GLenum internal_format,
                                         GLsizei width,
@@ -295,53 +310,39 @@
     const scoped_refptr<NativeImageBuffer>& image_buffer)
     : version_(version),
       target_(texture->target()),
-      image_buffer_(image_buffer.get()
-                        ? image_buffer
-                        : NativeImageBuffer::Create(texture->service_id())),
+      image_buffer_(image_buffer),
       min_filter_(texture->min_filter()),
       mag_filter_(texture->mag_filter()),
       wrap_s_(texture->wrap_s()),
       wrap_t_(texture->wrap_t()),
       usage_(texture->usage()),
-      immutable_(texture->IsImmutable()) {
-  // TODO
-  DCHECK(!texture->face_infos_.empty());
-  DCHECK(!texture->face_infos_[0].level_infos.empty());
-  DCHECK(!texture->NeedsMips());
-  DCHECK(texture->face_infos_[0].level_infos[0].width);
-  DCHECK(texture->face_infos_[0].level_infos[0].height);
+      immutable_(texture->IsImmutable()),
+      defined_(texture->IsDefined()) {
+  DCHECK_IMPLIES(image_buffer_.get(), defined_);
+  if (!image_buffer_.get() && defined_) {
+    image_buffer_ = NativeImageBuffer::Create(texture->service_id());
+    DCHECK(image_buffer_.get());
+  }
 
   const Texture::FaceInfo& first_face = texture->face_infos_[0];
-  scoped_refptr<gfx::GLImage> gl_image(
-      new GLImageSync(image_buffer_,
-                      gfx::Size(first_face.level_infos[0].width,
-                                first_face.level_infos[0].height)));
-  texture->SetLevelImage(NULL, target_, 0, gl_image.get());
+  if (image_buffer_.get()) {
+    scoped_refptr<gfx::GLImage> gl_image(
+        new GLImageSync(image_buffer_,
+                        gfx::Size(first_face.level_infos[0].width,
+                                  first_face.level_infos[0].height)));
+    texture->SetLevelImage(NULL, target_, 0, gl_image.get());
+  }
 
-  // TODO: all levels
-  level_infos_.clear();
   const Texture::LevelInfo& level = first_face.level_infos[0];
-  LevelInfo info(level.target,
-                 level.internal_format,
-                 level.width,
-                 level.height,
-                 level.depth,
-                 level.border,
-                 level.format,
-                 level.type,
-                 level.cleared);
-  std::vector<LevelInfo> infos;
-  infos.push_back(info);
-  level_infos_.push_back(infos);
+  level_info_ = LevelInfo(level.target, level.internal_format, level.width,
+                          level.height, level.depth, level.border, level.format,
+                          level.type, level.cleared);
 }
 
 TextureDefinition::~TextureDefinition() {
 }
 
 Texture* TextureDefinition::CreateTexture() const {
-  if (!image_buffer_.get())
-    return NULL;
-
   GLuint texture_id;
   glGenTextures(1, &texture_id);
 
@@ -367,28 +368,16 @@
   // though.
   glFlush();
 
-  texture->face_infos_.resize(1);
-  for (size_t i = 0; i < level_infos_.size(); i++) {
-    const LevelInfo& base_info = level_infos_[i][0];
-    const size_t levels_needed = TextureManager::ComputeMipMapCount(
-        base_info.target, base_info.width, base_info.height, base_info.depth);
-    DCHECK(level_infos_.size() <= levels_needed);
-    texture->face_infos_[0].level_infos.resize(levels_needed);
-    for (size_t n = 0; n < level_infos_.size(); n++) {
-      const LevelInfo& info = level_infos_[i][n];
-      texture->SetLevelInfo(NULL,
-                            info.target,
-                            i,
-                            info.internal_format,
-                            info.width,
-                            info.height,
-                            info.depth,
-                            info.border,
-                            info.format,
-                            info.type,
-                            info.cleared);
-    }
+  if (defined_) {
+    texture->face_infos_.resize(1);
+    texture->face_infos_[0].level_infos.resize(1);
+    texture->SetLevelInfo(NULL, level_info_.target, 0,
+                          level_info_.internal_format, level_info_.width,
+                          level_info_.height, level_info_.depth,
+                          level_info_.border, level_info_.format,
+                          level_info_.type, level_info_.cleared);
   }
+
   if (image_buffer_.get()) {
     texture->SetLevelImage(
         NULL,
@@ -396,7 +385,7 @@
         0,
         new GLImageSync(
             image_buffer_,
-            gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height)));
+            gfx::Size(level_info_.width, level_info_.height)));
   }
 
   texture->target_ = target_;
@@ -418,6 +407,10 @@
     return false;
   }
 
+  // Texture became defined.
+  if (!image_buffer_.get() && texture->IsDefined())
+    return false;
+
   // All structural changes should have orphaned the texture.
   if (image_buffer_.get() && !texture->GetLevelImage(texture->target(), 0))
     return false;
@@ -426,14 +419,7 @@
 }
 
 bool TextureDefinition::SafeToRenderFrom() const {
-  for (const std::vector<LevelInfo>& face_info : level_infos_) {
-    for (const LevelInfo& level_info : face_info) {
-      if (!level_info.cleared) {
-        return false;
-      }
-    }
-  }
-  return true;
+  return level_info_.cleared;
 }
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/texture_definition.h b/gpu/command_buffer/service/texture_definition.h
index 95f0fa2..dcab0b8 100644
--- a/gpu/command_buffer/service/texture_definition.h
+++ b/gpu/command_buffer/service/texture_definition.h
@@ -61,6 +61,7 @@
   bool SafeToRenderFrom() const;
 
   struct LevelInfo {
+    LevelInfo();
     LevelInfo(GLenum target,
               GLenum internal_format,
               GLsizei width,
@@ -83,8 +84,6 @@
     bool cleared;
   };
 
-  typedef std::vector<std::vector<LevelInfo> > LevelInfos;
-
   unsigned int version_;
   GLenum target_;
   scoped_refptr<NativeImageBuffer> image_buffer_;
@@ -94,7 +93,10 @@
   GLenum wrap_t_;
   GLenum usage_;
   bool immutable_;
-  LevelInfos level_infos_;
+  bool defined_;
+
+  // Only support textures with one face and one level.
+  LevelInfo level_info_;
 };
 
 }  // namespage gles2
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn
index 1d86e69..4d068ed 100644
--- a/gpu/config/BUILD.gn
+++ b/gpu/config/BUILD.gn
@@ -45,6 +45,9 @@
     "software_rendering_list_json.cc",
   ]
 
+  # TODO(jschuh): size_t to int.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GPU_IMPLEMENTATION" ]
 
   deps = [
@@ -69,9 +72,6 @@
         "//third_party/amd/amd_videocard_info_win.cc",
       ]
     }
-
-    # TODO(jschuh): size_t to int.
-    cflags = [ "/wd4267" ]
   }
   if (use_libpci) {
     defines += [ "USE_LIBPCI=1" ]
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index 53361eb..dfebf2b 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -19,7 +19,7 @@
 {
   "name": "gpu driver bug list",
   // Please update the version number whenever you change this file.
-  "version": "7.16",
+  "version": "7.17",
   "entries": [
     {
       "id": 1,
@@ -1159,6 +1159,23 @@
       "features": [
         "disable_post_sub_buffers_for_onscreen_surfaces"
       ]
+    },
+    {
+      "id": 102,
+      "description": "Adreno 420 driver loses FBO attachment contents on bound FBO deletion",
+      "cr_bugs": [457027],
+      "os": {
+        "type": "android",
+        "version": {
+          "op": ">",
+          "value": "5.0.2"
+        }
+      },
+      "gl_vendor": "Qualcomm.*",
+      "gl_renderer": ".*420",
+      "features": [
+        "unbind_attachments_on_bound_render_fbo_delete"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
index f03b7a2..9476bf2 100644
--- a/gpu/config/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -100,6 +100,8 @@
          swizzle_rgba_for_async_readpixels)                  \
   GPU_OP(TEXSUBIMAGE2D_FASTER_THAN_TEXIMAGE2D,               \
          texsubimage2d_faster_than_teximage2d)               \
+  GPU_OP(UNBIND_ATTACHMENTS_ON_BOUND_RENDER_FBO_DELETE,      \
+         unbind_attachments_on_bound_render_fbo_delete)      \
   GPU_OP(UNBIND_FBO_ON_CONTEXT_SWITCH,                       \
          unbind_fbo_on_context_switch)                       \
   GPU_OP(UNFOLD_SHORT_CIRCUIT_AS_TERNARY_OPERATION,          \
diff --git a/gpu/perftests/measurements.cc b/gpu/perftests/measurements.cc
index 270e459..f94d1cb 100644
--- a/gpu/perftests/measurements.cc
+++ b/gpu/perftests/measurements.cc
@@ -4,6 +4,8 @@
 
 #include "gpu/perftests/measurements.h"
 
+#include "base/logging.h"
+#include "gpu/command_buffer/service/gpu_timing.h"
 #include "testing/perf/perf_test.h"
 
 namespace gpu {
diff --git a/gpu/perftests/measurements.h b/gpu/perftests/measurements.h
index 15be422..1f72b00 100644
--- a/gpu/perftests/measurements.h
+++ b/gpu/perftests/measurements.h
@@ -9,10 +9,10 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/time/time.h"
-#include "gpu/command_buffer/service/gpu_timing.h"
-#include "ui/gl/gl_bindings.h"
 
 namespace gpu {
+class GPUTiming;
+class GPUTimer;
 struct Measurement {
   Measurement();
   Measurement(const Measurement& m);
diff --git a/gpu/perftests/texture_upload_perftest.cc b/gpu/perftests/texture_upload_perftest.cc
index f691c9e..2e9ef6c 100644
--- a/gpu/perftests/texture_upload_perftest.cc
+++ b/gpu/perftests/texture_upload_perftest.cc
@@ -6,8 +6,10 @@
 #include <vector>
 
 #include "base/containers/small_map.h"
+#include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/service/gpu_timing.h"
 #include "gpu/perftests/measurements.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
@@ -31,7 +33,7 @@
   varying vec2 v_texCoord;
   void main() {
     gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
-    v_texCoord = vec2((a_position.x + 1) * 0.5, (a_position.y + 1) * 0.5);
+    v_texCoord = vec2((a_position.x + 1.0) * 0.5, (a_position.y + 1.0) * 0.5);
   }
 );
 const char kFragmentShader[] =
@@ -96,12 +98,30 @@
   void SetUp() override {
     // Initialize an offscreen surface and a gl context.
     gfx::GLSurface::InitializeOneOff();
-    surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size_);
+    surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(4, 4));
     gl_context_ = gfx::GLContext::CreateGLContext(NULL,  // share_group
                                                   surface_.get(),
                                                   gfx::PreferIntegratedGpu);
-
     ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
+    glGenTextures(1, &color_texture_);
+    glBindTexture(GL_TEXTURE_2D, color_texture_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.width(), size_.height(), 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+    glGenFramebuffersEXT(1, &framebuffer_object_);
+    glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
+
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                              GL_TEXTURE_2D, color_texture_, 0);
+    DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+              glCheckFramebufferStatusEXT(GL_FRAMEBUFFER));
+
+    glViewport(0, 0, size_.width(), size_.height());
+
     if (gpu_timing_.Initialize(gl_context_.get())) {
       LOG(INFO) << "Gpu timing initialized with timer type: "
                 << gpu_timing_.GetTimerTypeName();
@@ -110,7 +130,6 @@
     } else {
       LOG(WARNING) << "Can't initialize gpu timing";
     }
-
     // Prepare a simple program and a vertex buffer that will be
     // used to draw a quad on the offscreen surface.
     vertex_shader_ = LoadShader(GL_VERTEX_SHADER, kVertexShader);
@@ -142,18 +161,14 @@
 
   void TearDown() override {
     ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
-    if (program_object_ != 0) {
-      glDeleteProgram(program_object_);
-    }
-    if (vertex_shader_ != 0) {
-      glDeleteShader(vertex_shader_);
-    }
-    if (fragment_shader_ != 0) {
-      glDeleteShader(fragment_shader_);
-    }
-    if (vertex_buffer_ != 0) {
-      glDeleteShader(vertex_buffer_);
-    }
+    glDeleteProgram(program_object_);
+    glDeleteShader(vertex_shader_);
+    glDeleteShader(fragment_shader_);
+    glDeleteShader(vertex_buffer_);
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
+    glDeleteFramebuffersEXT(1, &framebuffer_object_);
+    glDeleteTextures(1, &color_texture_);
 
     gl_context_ = nullptr;
     surface_ = nullptr;
@@ -167,6 +182,8 @@
                                          const GLenum format,
                                          const GLenum type) {
     ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
+    DCHECK_NE(0u, framebuffer_object_);
+    glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
 
     MeasurementTimers total_timers(&gpu_timing_);
     GLuint texture_id = 0;
@@ -178,8 +195,8 @@
 
     glTexImage2D(GL_TEXTURE_2D, 0, format, size_.width(), size_.height(), 0,
                  format, type, &pixels[0]);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     CheckNoGlError();
@@ -222,11 +239,13 @@
     return measurements;
   }
 
-  const gfx::Size size_;  // for the offscreen surface and the texture
+  const gfx::Size size_;  // for the fbo and the texture
   scoped_refptr<gfx::GLContext> gl_context_;
   scoped_refptr<gfx::GLSurface> surface_;
   GPUTiming gpu_timing_;
 
+  GLuint color_texture_ = 0;
+  GLuint framebuffer_object_ = 0;
   GLuint vertex_shader_ = 0;
   GLuint fragment_shader_ = 0;
   GLuint program_object_ = 0;
diff --git a/mojo/converters/surfaces/surfaces_type_converters.cc b/mojo/converters/surfaces/surfaces_type_converters.cc
index 0803ba6..fe40d32 100644
--- a/mojo/converters/surfaces/surfaces_type_converters.cc
+++ b/mojo/converters/surfaces/surfaces_type_converters.cc
@@ -42,9 +42,9 @@
     cc::YUVVideoDrawQuad::REC_601 ==
         static_cast<cc::YUVVideoDrawQuad::ColorSpace>(YUV_COLOR_SPACE_REC_601),
     rec_601_enum_matches);
-COMPILE_ASSERT(cc::YUVVideoDrawQuad::REC_601_JPEG ==
+COMPILE_ASSERT(cc::YUVVideoDrawQuad::JPEG ==
                    static_cast<cc::YUVVideoDrawQuad::ColorSpace>(
-                       YUV_COLOR_SPACE_REC_601_JPEG),
+                       YUV_COLOR_SPACE_JPEG),
                rec_601_jpeg_enum_matches);
 
 namespace {
diff --git a/mojo/services/surfaces/public/interfaces/quads.mojom b/mojo/services/surfaces/public/interfaces/quads.mojom
index 58f85e0..2418d51 100644
--- a/mojo/services/surfaces/public/interfaces/quads.mojom
+++ b/mojo/services/surfaces/public/interfaces/quads.mojom
@@ -79,7 +79,8 @@
 
 enum YUVColorSpace {
   REC_601,       // SDTV standard with restricted "studio swing" color range.
-  REC_601_JPEG,  // Full color range [0, 255] variant of the above.
+  REC_709,       // HDTV standard with restricted "studio swing" color range.
+  JPEG,          // Full color range [0, 255] JPEG color space.
 };
 
 struct YUVVideoQuadState {
diff --git a/mojo/tools/android_mojo_shell.py b/mojo/tools/android_mojo_shell.py
index 019d083..74225fa 100755
--- a/mojo/tools/android_mojo_shell.py
+++ b/mojo/tools/android_mojo_shell.py
@@ -39,13 +39,13 @@
                            default=True, action='store_true')
   debug_group.add_argument('--release', help='Release build', default=False,
                            dest='debug', action='store_false')
-  debug_group.add_argument('--target-arch',
+  debug_group.add_argument('--target-cpu',
                            help='CPU architecture to run for.',
                            choices=['x64', 'x86', 'arm'])
   launcher_args, args = parser.parse_known_args()
 
   config = Config(target_os=Config.OS_ANDROID,
-                  target_arch=launcher_args.target_arch,
+                  target_cpu=launcher_args.target_cpu,
                   is_debug=launcher_args.debug)
 
   args.append(android.PrepareShellRun(config))
diff --git a/mojo/tools/get_test_list.py b/mojo/tools/get_test_list.py
index 77e1744..70b4f2b 100755
--- a/mojo/tools/get_test_list.py
+++ b/mojo/tools/get_test_list.py
@@ -180,9 +180,9 @@
   if ShouldRunTest("nacl"):
     AddEntry("NaCl tests",
              [os.path.join(build_dir, "monacl_shell"),
-              os.path.join(build_dir, "irt_" + config.target_arch,
+              os.path.join(build_dir, "irt_" + config.target_cpu,
                            "irt_mojo.nexe"),
-              os.path.join(build_dir, "clang_newlib_" + config.target_arch,
+              os.path.join(build_dir, "clang_newlib_" + config.target_cpu,
                            "monacl_test.nexe")])
 
   # ----------------------------------------------------------------------------
diff --git a/mojo/tools/mojob.py b/mojo/tools/mojob.py
index 645171a..fb9e144 100755
--- a/mojo/tools/mojob.py
+++ b/mojo/tools/mojob.py
@@ -25,7 +25,7 @@
   elif args.chromeos:
     target_os = Config.OS_CHROMEOS
 
-  target_arch = args.target_arch
+  target_cpu = args.target_cpu
 
   additional_args = {}
 
@@ -64,7 +64,7 @@
   if 'test_results_server' in args:
     additional_args['test_results_server'] = args.test_results_server
 
-  return Config(target_os=target_os, target_arch=target_arch,
+  return Config(target_os=target_os, target_cpu=target_cpu,
                 is_debug=args.debug, dcheck_always_on=args.dcheck_always_on,
                 **additional_args)
 
@@ -191,7 +191,7 @@
   os_group.add_argument('--chromeos', help='Build for ChromeOS',
                         action='store_true')
 
-  parent_parser.add_argument('--target-arch',
+  parent_parser.add_argument('--target-cpu',
                              help='CPU architecture to build for.',
                              choices=['x64', 'x86', 'arm'])
 
diff --git a/mojo/tools/mopy/config.py b/mojo/tools/mopy/config.py
index c518988..7c8cadd 100644
--- a/mojo/tools/mopy/config.py
+++ b/mojo/tools/mopy/config.py
@@ -24,7 +24,7 @@
   OS_MAC = "mac"
   OS_WINDOWS = "windows"
 
-  # Valid values for target_arch (None is also valid):
+  # Valid values for target_cpu (None is also valid):
   ARCH_X86 = "x86"
   ARCH_X64 = "x64"
   ARCH_ARM = "arm"
@@ -39,7 +39,7 @@
   TEST_TYPE_PERF = "perf"
   TEST_TYPE_INTEGRATION = "integration"
 
-  def __init__(self, target_os=None, target_arch=None, is_debug=True,
+  def __init__(self, target_os=None, target_cpu=None, is_debug=True,
                is_clang=None, sanitizer=None, dcheck_always_on=False,
                **kwargs):
     """Constructs a Config with key-value pairs specified via keyword arguments.
@@ -47,7 +47,7 @@
 
     assert target_os in (None, Config.OS_ANDROID, Config.OS_CHROMEOS,
                          Config.OS_LINUX, Config.OS_MAC, Config.OS_WINDOWS)
-    assert target_arch in (None, Config.ARCH_X86, Config.ARCH_X64,
+    assert target_cpu in (None, Config.ARCH_X86, Config.ARCH_X64,
                            Config.ARCH_ARM)
     assert isinstance(is_debug, bool)
     assert is_clang is None or isinstance(is_clang, bool)
@@ -59,12 +59,12 @@
     self.values["target_os"] = (self.GetHostOS() if target_os is None else
                                 target_os)
 
-    if target_arch is None:
+    if target_cpu is None:
       if target_os == Config.OS_ANDROID:
-        target_arch = Config.ARCH_ARM
+        target_cpu = Config.ARCH_ARM
       else:
-        target_arch = self.GetHostCPUArch()
-    self.values["target_arch"] = target_arch
+        target_cpu = self.GetHostCPUArch()
+    self.values["target_cpu"] = target_cpu
 
     self.values["is_debug"] = is_debug
     self.values["is_clang"] = is_clang
@@ -104,9 +104,9 @@
     return self.values["target_os"]
 
   @property
-  def target_arch(self):
+  def target_cpu(self):
     """CPU arch of the build/test target."""
-    return self.values["target_arch"]
+    return self.values["target_cpu"]
 
   @property
   def is_debug(self):
diff --git a/mojo/tools/mopy/gn.py b/mojo/tools/mopy/gn.py
index aeaa135..831711b 100644
--- a/mojo/tools/mopy/gn.py
+++ b/mojo/tools/mopy/gn.py
@@ -22,8 +22,8 @@
   subdir = ""
   if config.target_os == Config.OS_ANDROID:
     subdir += "android_"
-    if config.target_arch != Config.ARCH_ARM:
-      subdir += config.target_arch + "_"
+    if config.target_cpu != Config.ARCH_ARM:
+      subdir += config.target_cpu + "_"
   elif config.target_os == Config.OS_CHROMEOS:
     subdir += "chromeos_"
   subdir += "Debug" if config.is_debug else "Release"
@@ -72,7 +72,7 @@
     gn_args["use_glib"] = False
     gn_args["use_system_harfbuzz"] = False
 
-  gn_args["target_arch"] = config.target_arch
+  gn_args["target_cpu"] = config.target_cpu
 
   return gn_args
 
@@ -103,7 +103,7 @@
     config_args["goma_dir"] = args.get("goma_dir")
   config_args["use_nacl"] = args.get("mojo_use_nacl", False)
   config_args["target_os"] = args.get("os")
-  config_args["target_arch"] = args.get("target_arch")
+  config_args["target_cpu"] = args.get("target_cpu")
   config_args["dcheck_always_on"] = args.get("dcheck_always_on")
   return Config(**config_args)
 
diff --git a/mojo/tools/mopy/gn_unittest.py b/mojo/tools/mopy/gn_unittest.py
index 815a63d..3773027 100644
--- a/mojo/tools/mopy/gn_unittest.py
+++ b/mojo/tools/mopy/gn_unittest.py
@@ -18,7 +18,7 @@
     """Tests that config to gn to config is the identity"""
     configs_to_test = {
       "target_os": [None, "android", "chromeos", "linux"],
-      "target_arch": [None, "x86", "x64", "arm"],
+      "target_cpu": [None, "x86", "x64", "arm"],
       "is_debug": [False, True],
       "is_clang": [False, True],
       "sanitizer": [None, Config.SANITIZER_ASAN],
@@ -37,7 +37,7 @@
     """Tests that gn to config to gn is the identity"""
     configs_to_test = {
       "os": [None, "android", "chromeos"],
-      "target_arch": ["x86", "x64", "arm"],
+      "target_cpu": ["x86", "x64", "arm"],
       "is_debug": [False, True],
       "is_clang": [False, True],
       "is_asan": [False, True],
diff --git a/mojo/tools/roll/cc_strip_video.patch b/mojo/tools/roll/cc_strip_video.patch
index 2d3fb51..827171b 100644
--- a/mojo/tools/roll/cc_strip_video.patch
+++ b/mojo/tools/roll/cc_strip_video.patch
@@ -1,5 +1,5 @@
 diff --git a/cc/BUILD.gn b/cc/BUILD.gn
-index 41f99e2..d6fd028 100644
+index 6fe0694..803b0f9 100644
 --- a/cc/BUILD.gn
 +++ b/cc/BUILD.gn
 @@ -222,13 +222,6 @@ component("cc") {
@@ -31,9 +31,9 @@
      "test/fake_ui_resource_layer_tree_host_impl.h",
 -    "test/fake_video_frame_provider.cc",
 -    "test/fake_video_frame_provider.h",
-     "test/failure_output_surface.cc",
-     "test/failure_output_surface.h",
      "test/geometry_test_utils.cc",
+     "test/geometry_test_utils.h",
+     "test/impl_side_painting_settings.h",
 @@ -783,7 +772,6 @@ if (!is_win || link_chrome_on_windows) {
        "layers/tiled_layer_unittest.cc",
        "layers/ui_resource_layer_impl_unittest.cc",
@@ -42,10 +42,10 @@
        "output/begin_frame_args_unittest.cc",
        "output/delegating_renderer_unittest.cc",
        "output/filter_operations_unittest.cc",
-@@ -815,7 +803,6 @@ if (!is_win || link_chrome_on_windows) {
-       "resources/texture_uploader_unittest.cc",
+@@ -816,7 +804,6 @@ if (!is_win || link_chrome_on_windows) {
        "resources/tile_manager_unittest.cc",
        "resources/tile_priority_unittest.cc",
+       "resources/tile_task_worker_pool_unittest.cc",
 -      "resources/video_resource_updater_unittest.cc",
        "scheduler/begin_frame_source_unittest.cc",
        "scheduler/delay_based_time_source_unittest.cc",
@@ -250,7 +250,7 @@
 -
 -}  // namespace cc
 diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
-index a394a1a..2612f10 100644
+index 6142d96..5c560dc 100644
 --- a/cc/output/gl_renderer.cc
 +++ b/cc/output/gl_renderer.cc
 @@ -13,7 +13,6 @@
@@ -262,7 +262,7 @@
  #include "cc/output/compositor_frame_metadata.h"
  #include "cc/output/context_provider.h"
 diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
-index 36765ce..d7d1211 100644
+index 743e1cf..9e9175e 100644
 --- a/cc/output/renderer_pixeltest.cc
 +++ b/cc/output/renderer_pixeltest.cc
 @@ -12,7 +12,6 @@
@@ -273,7 +273,7 @@
  #include "third_party/skia/include/core/SkColorPriv.h"
  #include "third_party/skia/include/core/SkImageFilter.h"
  #include "third_party/skia/include/core/SkMatrix.h"
-@@ -386,453 +385,6 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) {
+@@ -388,453 +387,6 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) {
        FuzzyPixelOffByOneComparator(true)));
  }
  
@@ -457,7 +457,7 @@
 -    const bool with_alpha = (video_frame->format() == media::VideoFrame::YV12A);
 -    const YUVVideoDrawQuad::ColorSpace color_space =
 -        (video_frame->format() == media::VideoFrame::YV12J
--             ? YUVVideoDrawQuad::REC_601_JPEG
+-             ? YUVVideoDrawQuad::JPEG
 -             : YUVVideoDrawQuad::REC_601);
 -    const gfx::Rect rect(shared_state->content_bounds);
 -    const gfx::Rect opaque_rect(0, 0, 0, 0);
@@ -728,7 +728,7 @@
    gfx::Rect viewport_rect(this->device_viewport_size_);
  
 diff --git a/cc/quads/yuv_video_draw_quad.h b/cc/quads/yuv_video_draw_quad.h
-index 99ed7e2..d57d56f 100644
+index 358929e..15bce98 100644
 --- a/cc/quads/yuv_video_draw_quad.h
 +++ b/cc/quads/yuv_video_draw_quad.h
 @@ -8,7 +8,6 @@
@@ -740,7 +740,7 @@
  
  namespace cc {
 diff --git a/cc/resources/drawing_display_item.cc b/cc/resources/drawing_display_item.cc
-index 0fe86f4..1ef149f 100644
+index 648f9de..6dffad9 100644
 --- a/cc/resources/drawing_display_item.cc
 +++ b/cc/resources/drawing_display_item.cc
 @@ -6,6 +6,7 @@
@@ -752,7 +752,7 @@
  #include "base/trace_event/trace_event_argument.h"
  #include "cc/debug/picture_debug_util.h"
 diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
-index aa46125..07fb048 100644
+index 70fa92b..38b2d66 100644
 --- a/cc/trees/layer_tree_host_impl_unittest.cc
 +++ b/cc/trees/layer_tree_host_impl_unittest.cc
 @@ -27,7 +27,6 @@
@@ -795,7 +795,7 @@
    }
  
    LayerTreeSettings DefaultSettings() {
-@@ -5489,18 +5484,6 @@ TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
+@@ -5463,18 +5458,6 @@ TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
    root_layer->SetBounds(gfx::Size(10, 10));
    root_layer->SetHasRenderSurface(true);
  
@@ -814,7 +814,7 @@
    scoped_ptr<IOSurfaceLayerImpl> io_surface_layer =
        IOSurfaceLayerImpl::Create(host_impl_->active_tree(), 5);
    io_surface_layer->SetBounds(gfx::Size(10, 10));
-@@ -6580,16 +6563,6 @@ TEST_F(LayerTreeHostImplTest,
+@@ -6555,16 +6538,6 @@ TEST_F(LayerTreeHostImplTest,
    scoped_ptr<SolidColorLayerImpl> root_layer =
        SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
  
@@ -832,7 +832,7 @@
    EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame));
    host_impl_->DrawLayers(&frame, gfx::FrameTime::Now());
 diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
-index bb315e7..bb6dc17 100644
+index 48be5b8..f9b690d 100644
 --- a/cc/trees/layer_tree_host_unittest.cc
 +++ b/cc/trees/layer_tree_host_unittest.cc
 @@ -18,7 +18,6 @@
@@ -851,7 +851,7 @@
  #include "cc/test/geometry_test_utils.h"
  #include "cc/test/impl_side_painting_settings.h"
  #include "cc/test/layer_tree_test.h"
-@@ -4202,28 +4200,6 @@ class LayerInvalidateCausesDraw : public LayerTreeHostTest {
+@@ -4167,28 +4165,6 @@ class LayerInvalidateCausesDraw : public LayerTreeHostTest {
    int num_draws_;
  };
  
@@ -881,7 +881,7 @@
  // to the compositor thread, even though no resources are updated in
  // response to that invalidation.
 diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
-index 596adc0..fb0c9c8 100644
+index c99180d..cc536f4 100644
 --- a/cc/trees/layer_tree_host_unittest_context.cc
 +++ b/cc/trees/layer_tree_host_unittest_context.cc
 @@ -15,8 +15,6 @@
@@ -919,7 +919,7 @@
    }
  
    void LoseContext() {
-@@ -1057,41 +1050,6 @@ class LayerTreeHostContextTestDontUseLostResources
+@@ -1055,41 +1048,6 @@ class LayerTreeHostContextTestDontUseLostResources
      layer_with_mask->SetMaskLayer(mask.get());
      root->AddChild(layer_with_mask);
  
@@ -961,7 +961,7 @@
      if (!delegating_renderer()) {
        // TODO(danakj): IOSurface layer can not be transported. crbug.com/239335
        scoped_refptr<IOSurfaceLayer> io_surface = IOSurfaceLayer::Create();
-@@ -1121,14 +1079,6 @@ class LayerTreeHostContextTestDontUseLostResources
+@@ -1119,14 +1077,6 @@ class LayerTreeHostContextTestDontUseLostResources
  
    void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
      LayerTreeHostContextTest::CommitCompleteOnThread(host_impl);
@@ -976,7 +976,7 @@
    }
  
    DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
-@@ -1177,14 +1127,6 @@ class LayerTreeHostContextTestDontUseLostResources
+@@ -1175,14 +1125,6 @@ class LayerTreeHostContextTestDontUseLostResources
    scoped_refptr<DelegatedFrameResourceCollection>
        delegated_resource_collection_;
    scoped_refptr<DelegatedFrameProvider> delegated_frame_provider_;
diff --git a/mojo/tools/roll/update_from_chromium.py b/mojo/tools/roll/update_from_chromium.py
index 9b67672..ace99d9 100755
--- a/mojo/tools/roll/update_from_chromium.py
+++ b/mojo/tools/roll/update_from_chromium.py
@@ -48,6 +48,7 @@
     "third_party/markupsafe",
     "third_party/mesa",
     "third_party/modp_b64",
+    "third_party/ots",
     "third_party/ply",
     "third_party/protobuf",
     "third_party/pymock",
diff --git a/mojo/tools/upload_binaries.py b/mojo/tools/upload_binaries.py
index 4acf03e..e0b86db 100755
--- a/mojo/tools/upload_binaries.py
+++ b/mojo/tools/upload_binaries.py
@@ -25,7 +25,7 @@
 ]
 
 def target(config):
-  return config.target_os + "-" + config.target_arch
+  return config.target_os + "-" + config.target_cpu
 
 def find_apps_to_upload(build_dir):
   apps = []
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index 8b18a3a..c4ce4fc 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -11,7 +11,7 @@
   compile_credentials = is_linux
 
   compile_seccomp_bpf_demo =
-      is_linux && (cpu_arch == "x86" || cpu_arch == "x64")
+      is_linux && (current_cpu == "x86" || current_cpu == "x64")
 }
 
 # We have two principal targets: sandbox and sandbox_linux_unittests
@@ -63,8 +63,10 @@
   }
 }
 
-# The main sandboxing test target.
-test("sandbox_linux_unittests") {
+# Sources shared by sandbox_linux_unittests and sandbox_linux_jni_unittests.
+source_set("sandbox_linux_unittests_sources") {
+  testonly = true
+
   sources = [
     "services/proc_util_unittest.cc",
     "services/resource_limits_unittests.cc",
@@ -127,23 +129,24 @@
   }
 }
 
-# TODO(GYP) Android version of this test.
-#    {
-#      # This target is the shared library used by Android APK (i.e.
-#      # JNI-friendly) tests.
-#      "target_name": "sandbox_linux_jni_unittests",
-#      "includes": [
-#        "sandbox_linux_test_sources.gypi",
-#      ],
-#      "type": "shared_library",
-#      "conditions": [
-#        [ "OS == "android"", {
-#          "dependencies": [
-#            "../testing/android/native_test.gyp:native_test_native_code",
-#          ],
-#        }],
-#      ],
-#    },
+# The main sandboxing test target.
+test("sandbox_linux_unittests") {
+  deps = [
+    ":sandbox_linux_unittests_sources",
+  ]
+}
+
+# This target is the shared library used by Android APK (i.e.
+# JNI-friendly) tests.
+shared_library("sandbox_linux_jni_unittests") {
+  testonly = true
+  deps = [
+    ":sandbox_linux_unittests_sources",
+  ]
+  if (is_android) {
+    deps += [ "//testing/android:native_test_native_code" ]
+  }
+}
 
 component("seccomp_bpf") {
   sources = [
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index e571dde..ed21fd1 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -114,7 +114,7 @@
   int status = -1;
   PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
 
-  return kExitSuccess == status;
+  return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
 }
 
 // CHECK() that an attempt to move to a new user namespace raised an expected
@@ -174,12 +174,14 @@
   // have disappeared. Make sure to not do anything in the child, as this is a
   // fragile execution environment.
   if (pid == 0) {
-    _exit(0);
+    _exit(kExitSuccess);
   }
 
   // Always reap the child.
-  siginfo_t infop;
-  PCHECK(0 == HANDLE_EINTR(waitid(P_PID, pid, &infop, WEXITED)));
+  int status = -1;
+  PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+  CHECK(WIFEXITED(status));
+  CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
 
   // clone(2) succeeded, we can use CLONE_NEWUSER.
   return true;
diff --git a/sandbox/linux/services/proc_util_unittest.cc b/sandbox/linux/services/proc_util_unittest.cc
index ee36c83..2bf37a0 100644
--- a/sandbox/linux/services/proc_util_unittest.cc
+++ b/sandbox/linux/services/proc_util_unittest.cc
@@ -28,9 +28,9 @@
   // No open directory should exist at startup.
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(-1));
   {
-    // Have a "/dev" file descriptor around.
-    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    base::ScopedFD dev_fd_closer(dev_fd);
+    // Have a "/proc" file descriptor around.
+    int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
+    base::ScopedFD proc_fd_closer(proc_fd);
     EXPECT_TRUE(ProcUtil::HasOpenDirectory(-1));
   }
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(-1));
@@ -48,14 +48,14 @@
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
 
   {
-    // Have a "/dev" file descriptor around.
-    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    base::ScopedFD dev_fd_closer(dev_fd);
+    // Have a directory file descriptor around.
+    int open_directory_fd = open("/proc/self", O_RDONLY | O_DIRECTORY);
+    base::ScopedFD open_directory_fd_closer(open_directory_fd);
     EXPECT_TRUE(ProcUtil::HasOpenDirectory(proc_fd));
   }
 
-  // The "/dev" file descriptor should now be closed, |proc_fd| is the only
-  // directory file descriptor open.
+  // The "/proc/self" file descriptor should now be closed, |proc_fd| is the
+  // only directory file descriptor open.
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
 }
 
diff --git a/services/surfaces/surfaces_scheduler.cc b/services/surfaces/surfaces_scheduler.cc
index f048d26..0d04294 100644
--- a/services/surfaces/surfaces_scheduler.cc
+++ b/services/surfaces/surfaces_scheduler.cc
@@ -92,4 +92,7 @@
     const cc::BeginFrameArgs& args) {
 }
 
+void SurfacesScheduler::SendBeginMainFrameNotExpectedSoon() {
+}
+
 }  // namespace mojo
diff --git a/services/surfaces/surfaces_scheduler.h b/services/surfaces/surfaces_scheduler.h
index e985973..6a1d4b3 100644
--- a/services/surfaces/surfaces_scheduler.h
+++ b/services/surfaces/surfaces_scheduler.h
@@ -43,6 +43,7 @@
   base::TimeDelta CommitToActivateDurationEstimate() override;
   void DidBeginImplFrameDeadline() override;
   void SendBeginFramesToChildren(const cc::BeginFrameArgs& args) override;
+  void SendBeginMainFrameNotExpectedSoon() override;
 
   Client* client_;
   scoped_ptr<cc::Scheduler> scheduler_;
diff --git a/shell/android/library_loader.cc b/shell/android/library_loader.cc
index 42e02df..0aff50b 100644
--- a/shell/android/library_loader.cc
+++ b/shell/android/library_loader.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/android/base_jni_onload.h"
 #include "base/android/base_jni_registrar.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_registrar.h"
-#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/logging.h"
+#include "base/bind.h"
 #include "services/native_viewport/platform_viewport_android.h"
 #include "shell/android/android_handler.h"
 #include "shell/android/keyboard_impl.h"
@@ -22,7 +22,10 @@
      native_viewport::PlatformViewportAndroid::Register},
 };
 
-bool RegisterMojoJni(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
+  if (!base::android::RegisterJni(env))
+    return false;
+
   return RegisterNativeMethods(env, kMojoRegisteredMethods,
                                arraysize(kMojoRegisteredMethods));
 }
@@ -31,17 +34,13 @@
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-
-  if (!base::android::RegisterLibraryLoaderEntryHook(env))
+  std::vector<base::android::RegisterCallback> register_callbacks;
+  register_callbacks.push_back(base::Bind(&RegisterJNI));
+  if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks) ||
+      !base::android::OnJNIOnLoadInit(
+          std::vector<base::android::InitCallback>())) {
     return -1;
-
-  if (!base::android::RegisterJni(env))
-    return -1;
-
-  if (!RegisterMojoJni(env))
-    return -1;
+  }
 
   return JNI_VERSION_1_4;
 }
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 85ccce7..aede7dc 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//testing/test.gni")
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
 
@@ -178,7 +178,7 @@
     defines += [ "SKIA_IMPLEMENTATION=1" ]
   }
 
-  if (cpu_arch == "arm") {
+  if (current_cpu == "arm") {
     if (arm_use_neon) {
       defines += [ "SK_ARM_HAS_NEON" ]
     }
@@ -298,7 +298,10 @@
   sources += gypi_skia_utils.sources
   sources += gypi_values.skia_library_sources
 
-  if (cpu_arch == "arm") {
+  # This and skia_opts are really the same conceptual target so share headers.
+  allow_circular_includes_from = [ ":skia_opts" ]
+
+  if (current_cpu == "arm") {
     sources += [
       "//third_party/skia/src/core/SkUtilsArm.cpp",
       "//third_party/skia/src/core/SkUtilsArm.h",
@@ -532,7 +535,7 @@
   cflags = []
   defines = []
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     sources = gypi_skia_opts.sse2_sources + gypi_skia_opts.ssse3_sources +
               gypi_skia_opts.sse41_sources +
               [
@@ -543,7 +546,7 @@
     if (is_linux || is_mac) {
       cflags += [ "-msse4.1" ]
     }
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     # The assembly uses the frame pointer register (r7 in Thumb/r11 in
     # ARM), the compiler doesn't like that.
     cflags += [ "-fomit-frame-pointer" ]
@@ -563,7 +566,7 @@
     } else {
       sources = gypi_skia_opts.none_sourcees
     }
-  } else if (cpu_arch == "mipsel") {
+  } else if (current_cpu == "mipsel") {
     cflags += [ "-fomit-frame-pointer" ]
     sources = gypi_skia_opts.none_sources
   } else {
@@ -611,6 +614,7 @@
     ":skia",
     "//base",
     "//base/test:run_all_unittests",
+    "//cc:test_support",  # TODO: Fix this test to not depend on cc.
     "//testing/gtest",
     "//ui/gfx",
     "//ui/gfx/geometry",
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 84a02d7..54c5f07 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -253,6 +253,10 @@
 #   define SK_IGNORE_ETC1_SUPPORT
 #endif
 
+#ifndef    SK_SUPPORT_LEGACY_MIPMAP_EFFECTIVE_SCALE
+#   define SK_SUPPORT_LEGACY_MIPMAP_EFFECTIVE_SCALE
+#endif
+
 #ifndef    SK_IGNORE_GPU_DITHER
 #   define SK_IGNORE_GPU_DITHER
 #endif
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc
index 376d173..fe447c7 100644
--- a/skia/ext/analysis_canvas.cc
+++ b/skia/ext/analysis_canvas.cc
@@ -9,7 +9,6 @@
 #include "third_party/skia/include/core/SkRRect.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/src/core/SkRasterClip.h"
-#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace {
 
diff --git a/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc
index ff7c2ad..9c7966d 100644
--- a/skia/ext/bitmap_platform_device_mac.cc
+++ b/skia/ext/bitmap_platform_device_mac.cc
@@ -71,6 +71,78 @@
   config_dirty_ = true;
 }
 
+// Loads the specified Skia transform into the device context
+static void LoadTransformToCGContext(CGContextRef context,
+                                     const SkMatrix& matrix) {
+  // CoreGraphics can concatenate transforms, but not reset the current one.
+  // So in order to get the required behavior here, we need to first make
+  // the current transformation matrix identity and only then load the new one.
+
+  // Reset matrix to identity.
+  CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
+  CGAffineTransform orig_cg_matrix_inv =
+      CGAffineTransformInvert(orig_cg_matrix);
+  CGContextConcatCTM(context, orig_cg_matrix_inv);
+
+  // assert that we have indeed returned to the identity Matrix.
+  SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
+
+  // Convert xform to CG-land.
+  // Our coordinate system is flipped to match WebKit's so we need to modify
+  // the xform to match that.
+  SkMatrix transformed_matrix = matrix;
+  SkScalar sy = -matrix.getScaleY();
+  transformed_matrix.setScaleY(sy);
+  size_t height = CGBitmapContextGetHeight(context);
+  SkScalar ty = -matrix.getTranslateY();  // y axis is flipped.
+  transformed_matrix.setTranslateY(ty + (SkScalar)height);
+
+  CGAffineTransform cg_matrix =
+      gfx::SkMatrixToCGAffineTransform(transformed_matrix);
+
+  // Load final transform into context.
+  CGContextConcatCTM(context, cg_matrix);
+}
+
+// Loads a SkRegion into the CG context.
+static void LoadClippingRegionToCGContext(CGContextRef context,
+                                          const SkRegion& region,
+                                          const SkMatrix& transformation) {
+  if (region.isEmpty()) {
+    // region can be empty, in which case everything will be clipped.
+    SkRect rect;
+    rect.setEmpty();
+    CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
+  } else if (region.isRect()) {
+    // CoreGraphics applies the current transform to clip rects, which is
+    // unwanted. Inverse-transform the rect before sending it to CG. This only
+    // works for translations and scaling, but not for rotations (but the
+    // viewport is never rotated anyway).
+    SkMatrix t;
+    bool did_invert = transformation.invert(&t);
+    if (!did_invert)
+      t.reset();
+    // Do the transformation.
+    SkRect rect;
+    rect.set(region.getBounds());
+    t.mapRect(&rect);
+    SkIRect irect;
+    rect.round(&irect);
+    CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
+  } else {
+    // It is complex.
+    SkPath path;
+    region.getBoundaryPath(&path);
+    // Clip. Note that windows clipping regions are not affected by the
+    // transform so apply it manually.
+    path.transform(transformation);
+    // TODO(playmobil): Implement.
+    SkASSERT(false);
+    // LoadPathToDC(context, path);
+    // hrgn = PathToRegion(context);
+  }
+}
+
 void BitmapPlatformDevice::LoadConfig() {
   if (!config_dirty_ || !bitmap_context_)
     return;  // Nothing to do.
diff --git a/skia/ext/platform_canvas_unittest.cc b/skia/ext/platform_canvas_unittest.cc
index 9ab5667..530d722 100644
--- a/skia/ext/platform_canvas_unittest.cc
+++ b/skia/ext/platform_canvas_unittest.cc
@@ -418,7 +418,9 @@
   EXPECT_EQ(kN32_SkColorType,  // Same for all platforms.
             platform_bitmap->GetBitmap().colorType());
   EXPECT_TRUE(platform_bitmap->GetBitmap().lockPixelsAreWritable());
+#if defined(SK_DEBUG)
   EXPECT_TRUE(platform_bitmap->GetBitmap().pixelRef()->isLocked());
+#endif
   EXPECT_TRUE(platform_bitmap->GetBitmap().pixelRef()->unique());
 
   *(platform_bitmap->GetBitmap().getAddr32(10, 20)) = 0xDEED1020;
diff --git a/skia/ext/platform_device.h b/skia/ext/platform_device.h
index 8486102..c903c87 100644
--- a/skia/ext/platform_device.h
+++ b/skia/ext/platform_device.h
@@ -131,19 +131,6 @@
   // have to be created twice.  If src_rect is null, then the entirety of the
   // source device will be copied.
   virtual void DrawToHDC(HDC, int x, int y, const RECT* src_rect);
-    
-#elif defined(OS_MACOSX)
-  // Loads a SkPath into the CG context. The path can there after be used for
-  // clipping or as a stroke.
-  static void LoadPathToCGContext(CGContextRef context, const SkPath& path);
-
-  // Initializes the default settings and colors in a device context.
-  static void InitializeCGContext(CGContextRef context);
-
-  // Loads a SkRegion into the CG context.
-  static void LoadClippingRegionToCGContext(CGContextRef context,
-                                            const SkRegion& region,
-                                            const SkMatrix& transformation);
 #endif
 
  protected:
@@ -161,10 +148,6 @@
 
   // Transforms SkPath's paths into a series of cubic path.
   static bool SkPathToCubicPaths(CubicPaths* paths, const SkPath& skpath);
-#elif defined(OS_MACOSX)
-  // Loads the specified Skia transform into the device context
-  static void LoadTransformToCGContext(CGContextRef context,
-                                       const SkMatrix& matrix);
 #endif
 };
 
diff --git a/skia/ext/platform_device_mac.cc b/skia/ext/platform_device_mac.cc
index f66372b..065b767 100644
--- a/skia/ext/platform_device_mac.cc
+++ b/skia/ext/platform_device_mac.cc
@@ -30,126 +30,4 @@
   // Flushing will be done in onAccessBitmap.
 }
 
-// Set up the CGContextRef for peaceful coexistence with Skia
-void PlatformDevice::InitializeCGContext(CGContextRef context) {
-  // CG defaults to the same settings as Skia
-}
-
-// static
-void PlatformDevice::LoadPathToCGContext(CGContextRef context,
-                                         const SkPath& path) {
-  // instead of a persistent attribute of the context, CG specifies the fill
-  // type per call, so we just have to load up the geometry.
-  CGContextBeginPath(context);
-
-  SkPoint points[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-  SkPath::Iter iter(path, false);
-  for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb;
-       verb = iter.next(points)) {
-    switch (verb) {
-      case SkPath::kMove_Verb: {  // iter.next returns 1 point
-        CGContextMoveToPoint(context, points[0].fX, points[0].fY);
-        break;
-      }
-      case SkPath::kLine_Verb: {  // iter.next returns 2 points
-        CGContextAddLineToPoint(context, points[1].fX, points[1].fY);
-        break;
-      }
-      case SkPath::kQuad_Verb: {  // iter.next returns 3 points
-        CGContextAddQuadCurveToPoint(context, points[1].fX, points[1].fY,
-                                     points[2].fX, points[2].fY);
-        break;
-      }
-      case SkPath::kCubic_Verb: {  // iter.next returns 4 points
-        CGContextAddCurveToPoint(context, points[1].fX, points[1].fY,
-                                 points[2].fX, points[2].fY,
-                                 points[3].fX, points[3].fY);
-        break;
-      }
-      case SkPath::kClose_Verb: {  // iter.next returns 1 point (the last point)
-        break;
-      }
-      case SkPath::kDone_Verb:  // iter.next returns 0 points
-      default: {
-        SkASSERT(false);
-        break;
-      }
-    }
-  }
-  CGContextClosePath(context);
-}
-
-// static
-void PlatformDevice::LoadTransformToCGContext(CGContextRef context,
-                                              const SkMatrix& matrix) {
-  // CoreGraphics can concatenate transforms, but not reset the current one.
-  // So in order to get the required behavior here, we need to first make
-  // the current transformation matrix identity and only then load the new one.
-
-  // Reset matrix to identity.
-  CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
-  CGAffineTransform orig_cg_matrix_inv = CGAffineTransformInvert(
-      orig_cg_matrix);
-  CGContextConcatCTM(context, orig_cg_matrix_inv);
-
-  // assert that we have indeed returned to the identity Matrix.
-  SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
-
-  // Convert xform to CG-land.
-  // Our coordinate system is flipped to match WebKit's so we need to modify
-  // the xform to match that.
-  SkMatrix transformed_matrix = matrix;
-  SkScalar sy = matrix.getScaleY() * (SkScalar)-1;
-  transformed_matrix.setScaleY(sy);
-  size_t height = CGBitmapContextGetHeight(context);
-  SkScalar ty = -matrix.getTranslateY(); // y axis is flipped.
-  transformed_matrix.setTranslateY(ty + (SkScalar)height);
-
-  CGAffineTransform cg_matrix = gfx::SkMatrixToCGAffineTransform(
-      transformed_matrix);
-
-  // Load final transform into context.
-  CGContextConcatCTM(context, cg_matrix);
-}
-
-// static
-void PlatformDevice::LoadClippingRegionToCGContext(
-         CGContextRef context,
-         const SkRegion& region,
-         const SkMatrix& transformation) {
-  if (region.isEmpty()) {
-    // region can be empty, in which case everything will be clipped.
-    SkRect rect;
-    rect.setEmpty();
-    CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
-  } else if (region.isRect()) {
-    // CoreGraphics applies the current transform to clip rects, which is
-    // unwanted. Inverse-transform the rect before sending it to CG. This only
-    // works for translations and scaling, but not for rotations (but the
-    // viewport is never rotated anyway).
-    SkMatrix t;
-    bool did_invert = transformation.invert(&t);
-    if (!did_invert)
-      t.reset();
-    // Do the transformation.
-    SkRect rect;
-    rect.set(region.getBounds());
-    t.mapRect(&rect);
-    SkIRect irect;
-    rect.round(&irect);
-    CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
-  } else {
-    // It is complex.
-    SkPath path;
-    region.getBoundaryPath(&path);
-    // Clip. Note that windows clipping regions are not affected by the
-    // transform so apply it manually.
-    path.transform(transformation);
-    // TODO(playmobil): Implement.
-    SkASSERT(false);
-    // LoadPathToDC(context, path);
-    // hrgn = PathToRegion(context);
-  }
-}
-
 }  // namespace skia
diff --git a/skia/ext/skia_utils_ios_unittest.mm b/skia/ext/skia_utils_ios_unittest.mm
index c199304..8d0c9be 100644
--- a/skia/ext/skia_utils_ios_unittest.mm
+++ b/skia/ext/skia_utils_ios_unittest.mm
@@ -8,7 +8,6 @@
 #import <UIKit/UIKit.h>
 
 #include "base/base64.h"
-#include "base/ios/ios_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/skia/skia_test_expectations.txt b/skia/skia_test_expectations.txt
index 4327f6e..5927d5c 100644
--- a/skia/skia_test_expectations.txt
+++ b/skia/skia_test_expectations.txt
@@ -48,4 +48,7 @@
 #
 # START OVERRIDES HERE
 
+# We fixed a blending bug.
+crbug.com/459579 virtual/gpu/fast/canvas/canvas-composite-transformclip.html [ ImageOnlyFailure ]
+
 # END OVERRIDES HERE (this line ensures that the file is newline-terminated)
diff --git a/sky/engine/platform/graphics/DecodingImageGenerator.cpp b/sky/engine/platform/graphics/DecodingImageGenerator.cpp
index 599e77c..f2e152c 100644
--- a/sky/engine/platform/graphics/DecodingImageGenerator.cpp
+++ b/sky/engine/platform/graphics/DecodingImageGenerator.cpp
@@ -65,7 +65,7 @@
     return true;
 }
 
-bool DecodingImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount)
+SkImageGenerator::Result DecodingImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount)
 {
     TRACE_EVENT1("blink", "DecodingImageGenerator::getPixels", "index", static_cast<int>(m_frameIndex));
 
@@ -73,11 +73,11 @@
     if (info.width() != m_imageInfo.width() || info.height() != m_imageInfo.height() || info.colorType() != m_imageInfo.colorType()) {
         // ImageFrame may have changed the owning SkBitmap to kOpaque_SkAlphaType after sniffing the encoded data, so if we see a request
         // for opaque, that is ok even if our initial alphatype was not opaque.
-        return false;
+        return Result::kInvalidScale;
     }
 
     bool decoded = m_frameGenerator->decodeAndScale(m_imageInfo, m_frameIndex, pixels, rowBytes);
-    return decoded;
+    return decoded ? Result::kSuccess : Result::kInvalidInput;
 }
 
 bool DecodingImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3])
diff --git a/sky/engine/platform/graphics/DecodingImageGenerator.h b/sky/engine/platform/graphics/DecodingImageGenerator.h
index 3d6ae39..a2e948e 100644
--- a/sky/engine/platform/graphics/DecodingImageGenerator.h
+++ b/sky/engine/platform/graphics/DecodingImageGenerator.h
@@ -51,7 +51,7 @@
 protected:
     virtual SkData* onRefEncodedData() override;
     virtual bool onGetInfo(SkImageInfo*) override;
-    virtual bool onGetPixels(const SkImageInfo&, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) override;
+    virtual Result onGetPixels(const SkImageInfo&, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) override;
     virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) override;
 
 private:
diff --git a/testing/android/native_test.gyp b/testing/android/native_test.gyp
index 7d0ebc8..632d687 100644
--- a/testing/android/native_test.gyp
+++ b/testing/android/native_test.gyp
@@ -16,13 +16,6 @@
             'native_test_launcher.cc',
             'native_test_launcher.h',
           ],
-          'direct_dependent_settings': {
-            'ldflags!': [
-              # JNI_OnLoad is implemented in a .a and we need to
-              # re-export in the .so.
-              '-Wl,--exclude-libs=ALL',
-            ],
-          },
           'dependencies': [
             '../../base/base.gyp:base',
             '../../base/base.gyp:test_support_base',
diff --git a/testing/android/native_test_jni_onload.cc b/testing/android/native_test_jni_onload.cc
index bfab3c9..de42356 100644
--- a/testing/android/native_test_jni_onload.cc
+++ b/testing/android/native_test_jni_onload.cc
@@ -4,22 +4,16 @@
 
 #include "base/android/base_jni_onload.h"
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "testing/android/native_test_launcher.h"
 
 namespace {
 
-class NativeTestJNIOnLoadDelegate : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool NativeTestJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return RegisterNativeTestJNI(env);
 }
 
-bool NativeTestJNIOnLoadDelegate::Init() {
+bool Init() {
   InstallHandlers();
   return true;
 }
@@ -29,11 +23,15 @@
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  NativeTestJNIOnLoadDelegate delegate;
-  std::vector<base::android::JNIOnLoadDelegate*> delegates;
-  delegates.push_back(&delegate);
+  std::vector<base::android::RegisterCallback> register_callbacks;
+  register_callbacks.push_back(base::Bind(&RegisterJNI));
 
-  if (!base::android::OnJNIOnLoad(vm, &delegates))
+  if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks))
+    return -1;
+
+  std::vector<base::android::InitCallback> init_callbacks;
+  init_callbacks.push_back(base::Bind(&Init));
+  if (!base::android::OnJNIOnLoadInit(init_callbacks))
     return -1;
 
   return JNI_VERSION_1_4;
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 1d15ec1..71e67ad 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1048,5 +1048,85 @@
         "script": "nacl_integration.py"
       }
     ]
+  },
+  "ClangToTLinuxASan tester": {
+    "gtest_tests": [
+      "accessibility_unittests",
+      "extensions_browsertests",
+      {
+        "test": "base_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "test": "browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        }
+      },
+      "cacheinvalidation_unittests",
+      "cast_unittests",
+      "cc_unittests",
+      "components_unittests",
+      {
+        "test": "content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "test": "content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "test": "crypto_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      "device_unittests",
+      "display_unittests",
+      "extensions_unittests",
+      "gcm_unit_tests",
+      "gfx_unittests",
+      "google_apis_unittests",
+      "gpu_unittests",
+      {
+        "test": "interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      "ipc_tests",
+      "jingle_unittests",
+      "media_unittests",
+      {
+        "test": "net_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 4
+        }
+      },
+      "ppapi_unittests",
+      "printing_unittests",
+      "remoting_unittests",
+      "sandbox_linux_unittests",
+      "skia_unittests",
+      "sql_unittests",
+      "sync_unit_tests",
+      "ui_base_unittests",
+      {
+        "test": "unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        }
+      },
+      "url_unittests"
+    ]
   }
 }
diff --git a/testing/chromoting/browser_test_commands_linux.txt b/testing/chromoting/browser_test_commands_linux.txt
index d6f2bf5..49f503e 100644
--- a/testing/chromoting/browser_test_commands_linux.txt
+++ b/testing/chromoting/browser_test_commands_linux.txt
@@ -3,6 +3,9 @@
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gafyd
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=non-gmail
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting
+/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Local_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile
+/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Local_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile
+/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_v2_Alive_OnLostFocus --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_Connect --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_InvalidAccessCode --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile
\ No newline at end of file
diff --git a/testing/commit_queue/config.json b/testing/commit_queue/config.json
index 1b05f1c..da48bda 100644
--- a/testing/commit_queue/config.json
+++ b/testing/commit_queue/config.json
@@ -8,7 +8,8 @@
                         "cast_shell": ["defaulttests"],
                         "cast_shell_apk": ["defaulttests"],
                         "linux_android_rel_ng": ["defaulttests"],
-                        "linux_chromium_asan_rel_ng": ["defaulttests"]
+                        "linux_chromium_asan_rel_ng": ["defaulttests"],
+                        "linux_chromium_clobber_rel_ng": ["defaulttests"]
                     },
                     "tryserver.chromium.mac": {
                         "mac_chromium_gn_rel": ["defaulttests"]
diff --git a/testing/legion/common_lib.py b/testing/legion/common_lib.py
index 2f527ea..c752e0f 100644
--- a/testing/legion/common_lib.py
+++ b/testing/legion/common_lib.py
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Common library methods used by both host and client controllers."""
+"""Common library methods used by both coordinator and task machines."""
 
 import argparse
 import logging
diff --git a/testing/legion/discovery_server.py b/testing/legion/discovery_server.py
deleted file mode 100644
index 94786ce..0000000
--- a/testing/legion/discovery_server.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""The discovery server used to register clients.
-
-The discovery server is started by the host controller and allows the clients
-to register themselves when they start. Authentication of the client controllers
-is based on an OTP passed to the client controller binary on startup.
-"""
-
-import logging
-import threading
-import xmlrpclib
-import SimpleXMLRPCServer
-
-#pylint: disable=relative-import
-import common_lib
-
-
-class DiscoveryServer(object):
-  """Discovery server run on the host."""
-
-  def __init__(self):
-    self._expected_clients = {}
-    self._rpc_server = None
-    self._thread = None
-
-  def _RegisterClientRPC(self, otp, ip):
-    """The RPC used by a client to register with the discovery server."""
-    assert otp in self._expected_clients
-    cb = self._expected_clients.pop(otp)
-    cb(ip)
-
-  def RegisterClientCallback(self, otp, callback):
-    """Registers a callback associated with an OTP."""
-    assert callable(callback)
-    self._expected_clients[otp] = callback
-
-  def Start(self):
-    """Starts the discovery server."""
-    logging.debug('Starting discovery server')
-    self._rpc_server = SimpleXMLRPCServer.SimpleXMLRPCServer(
-        (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT),
-        allow_none=True, logRequests=False)
-    self._rpc_server.register_function(
-        self._RegisterClientRPC, 'RegisterClient')
-    self._thread = threading.Thread(target=self._rpc_server.serve_forever)
-    self._thread.start()
-
-  def Shutdown(self):
-    """Shuts the discovery server down."""
-    if self._thread and self._thread.is_alive():
-      logging.debug('Shutting down discovery server')
-      self._rpc_server.shutdown()
diff --git a/testing/legion/examples/hello_world/client_test.isolate b/testing/legion/examples/hello_world/client_test.isolate
deleted file mode 100644
index 7135ef2..0000000
--- a/testing/legion/examples/hello_world/client_test.isolate
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'includes': [
-    '../../legion.isolate'
-  ],
-  'conditions': [
-    ['multi_machine == 1', {
-      'variables': {
-        'command': [
-          'python',
-          '../../client_controller.py',
-        ],
-        'files': [
-          'client_test.py',
-          'client_test.isolate'
-        ],
-      },
-    }],
-  ],
-}
diff --git a/testing/legion/examples/hello_world/host_test.isolate b/testing/legion/examples/hello_world/host_test.isolate
deleted file mode 100644
index da4ee4e..0000000
--- a/testing/legion/examples/hello_world/host_test.isolate
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'includes': [
-    '../../legion.isolate',
-    'client_test.isolate'
-  ],
-  'conditions': [
-    ['multi_machine == 1', {
-      'variables': {
-        'command': [
-          'host_test.py',
-        ],
-        'files': [
-          'host_test.py',
-        ],
-      },
-    }],
-  ]
-}
diff --git a/testing/legion/examples/hello_world/host_test.py b/testing/legion/examples/hello_world/host_test.py
deleted file mode 100755
index 7a7875b..0000000
--- a/testing/legion/examples/hello_world/host_test.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""A simple host test module.
-
-This module runs on the host machine and is responsible for creating 2
-client machines, waiting for them, and running RPC calls on them.
-"""
-
-# Map the legion directory so we can import the host controller.
-import sys
-sys.path.append('../../')
-
-import logging
-import time
-
-import host_controller
-
-
-class ExampleController(host_controller.HostController):
-  """A simple example controller for a test."""
-
-  def __init__(self):
-    super(ExampleController, self).__init__()
-    self.client1 = None
-    self.client2 = None
-
-  def CreateClient(self):
-    """Create a client object and set the proper values."""
-    client = self.NewClient(
-        isolate_file='client_test.isolate',
-        config_vars={'multi_machine': '1'},
-        dimensions={'os': 'legion-linux'}, priority=200,
-        idle_timeout_secs=90, connection_timeout_secs=90,
-        verbosity=logging.INFO)
-    client.Create()
-    return client
-
-  def SetUp(self):
-    """Create the client machines and wait until they connect.
-
-    In this call the actual creation of the client machines is done in parallel
-    by the system. The WaitForConnect calls are performed in series but will
-    return as soon as the clients connect.
-    """
-    self.client1 = self.CreateClient()
-    self.client2 = self.CreateClient()
-    self.client1.WaitForConnection()
-    self.client2.WaitForConnection()
-
-  def Task(self):
-    """Main method to run the task code."""
-    self.CallEcho(self.client1)
-    self.CallEcho(self.client2)
-    self.CallClientTest(self.client1)
-    self.CallClientTest(self.client2)
-
-  def CallEcho(self, client):
-    """Call rpc.Echo on a client."""
-    logging.info('Calling Echo on %s', client.name)
-    logging.info(client.rpc.Echo(client.name))
-
-  def CallClientTest(self, client):
-    """Call client_test.py name on a client."""
-    logging.info('Calling Subprocess to run "./client_test.py %s"', client.name)
-    proc = client.rpc.subprocess.Popen(['./client_test.py', client.name])
-    client.rpc.subprocess.Wait(proc)
-    retcode = client.rpc.subprocess.GetReturncode(proc)
-    stdout = client.rpc.subprocess.ReadStdout(proc)
-    stderr = client.rpc.subprocess.ReadStderr(proc)
-    logging.info('retcode: %s, stdout: %s, stderr: %s', retcode, stdout, stderr)
-
-
-if __name__ == '__main__':
-  ExampleController().RunController()
diff --git a/testing/legion/examples/subprocess/client.isolate b/testing/legion/examples/hello_world/task_test.isolate
similarity index 81%
copy from testing/legion/examples/subprocess/client.isolate
copy to testing/legion/examples/hello_world/task_test.isolate
index 611562c..1322f31 100644
--- a/testing/legion/examples/subprocess/client.isolate
+++ b/testing/legion/examples/hello_world/task_test.isolate
@@ -11,10 +11,11 @@
       'variables': {
         'command': [
           'python',
-          '../../client_controller.py',
+          '../../run_task.py',
         ],
         'files': [
-          'client.isolate'
+          'task_test.isolate',
+          'task_test.py',
         ],
       },
     }],
diff --git a/testing/legion/examples/hello_world/client_test.py b/testing/legion/examples/hello_world/task_test.py
similarity index 100%
rename from testing/legion/examples/hello_world/client_test.py
rename to testing/legion/examples/hello_world/task_test.py
diff --git a/testing/legion/examples/subprocess/subprocess_test.isolate b/testing/legion/examples/subprocess/subprocess_test.isolate
index 5b20167..6b4561f 100644
--- a/testing/legion/examples/subprocess/subprocess_test.isolate
+++ b/testing/legion/examples/subprocess/subprocess_test.isolate
@@ -5,7 +5,7 @@
 {
   'includes': [
     '../../legion.isolate',
-    'client.isolate'
+    'task.isolate'
   ],
   'conditions': [
     ['multi_machine == 1', {
diff --git a/testing/legion/examples/subprocess/subprocess_test.py b/testing/legion/examples/subprocess/subprocess_test.py
index 6d8ce87..28e3fb8 100755
--- a/testing/legion/examples/subprocess/subprocess_test.py
+++ b/testing/legion/examples/subprocess/subprocess_test.py
@@ -13,29 +13,29 @@
 import time
 import xmlrpclib
 
-import host_controller
+import test_controller
 
 
-class ExampleController(host_controller.HostController):
+class ExampleTestController(test_controller.TestController):
   """An example controller using the remote subprocess functions."""
 
   def __init__(self):
-    super(ExampleController, self).__init__()
-    self.client = None
+    super(ExampleTestController, self).__init__()
+    self.task = None
 
   def SetUp(self):
-    """Creates the client machine and waits until it connects."""
-    self.client = self.NewClient(
-        isolate_file='client.isolate',
+    """Creates the task machine and waits until it connects."""
+    self.task = self.CreateNewTask(
+        isolate_file='task.isolate',
         config_vars={'multi_machine': '1'},
         dimensions={'os': 'legion-linux'},
         idle_timeout_secs=90, connection_timeout_secs=90,
         verbosity=logging.DEBUG)
-    self.client.Create()
-    self.client.WaitForConnection()
+    self.task.Create()
+    self.task.WaitForConnection()
 
-  def Task(self):
-    """Main method to run the task code."""
+  def RunTest(self):
+    """Main method to run the test code."""
     self.TestLs()
     self.TestTerminate()
     self.TestMultipleProcesses()
@@ -43,37 +43,37 @@
   def TestMultipleProcesses(self):
     start = time.time()
 
-    sleep20 = self.client.rpc.subprocess.Popen(['sleep', '20'])
-    sleep10 = self.client.rpc.subprocess.Popen(['sleep', '10'])
+    sleep20 = self.task.rpc.subprocess.Popen(['sleep', '20'])
+    sleep10 = self.task.rpc.subprocess.Popen(['sleep', '10'])
 
-    self.client.rpc.subprocess.Wait(sleep10)
+    self.task.rpc.subprocess.Wait(sleep10)
     elapsed = time.time() - start
     assert elapsed >= 10 and elapsed < 11
 
-    self.client.rpc.subprocess.Wait(sleep20)
+    self.task.rpc.subprocess.Wait(sleep20)
     elapsed = time.time() - start
     assert elapsed >= 20
 
-    self.client.rpc.subprocess.Delete(sleep20)
-    self.client.rpc.subprocess.Delete(sleep10)
+    self.task.rpc.subprocess.Delete(sleep20)
+    self.task.rpc.subprocess.Delete(sleep10)
 
   def TestTerminate(self):
     start = time.time()
-    proc = self.client.rpc.subprocess.Popen(['sleep', '20'])
-    self.client.rpc.subprocess.Terminate(proc)  # Implicitly deleted
+    proc = self.task.rpc.subprocess.Popen(['sleep', '20'])
+    self.task.rpc.subprocess.Terminate(proc)  # Implicitly deleted
     try:
-      self.client.rpc.subprocess.Wait(proc)
+      self.task.rpc.subprocess.Wait(proc)
     except xmlrpclib.Fault:
       pass
     assert time.time() - start < 20
 
   def TestLs(self):
-    proc = self.client.rpc.subprocess.Popen(['ls'])
-    self.client.rpc.subprocess.Wait(proc)
-    assert self.client.rpc.subprocess.GetReturncode(proc) == 0
-    assert 'client.isolate' in self.client.rpc.subprocess.ReadStdout(proc)
-    self.client.rpc.subprocess.Delete(proc)
+    proc = self.task.rpc.subprocess.Popen(['ls'])
+    self.task.rpc.subprocess.Wait(proc)
+    assert self.task.rpc.subprocess.GetReturncode(proc) == 0
+    assert 'task.isolate' in self.task.rpc.subprocess.ReadStdout(proc)
+    self.task.rpc.subprocess.Delete(proc)
 
 
 if __name__ == '__main__':
-  ExampleController().RunController()
+  ExampleTestController().RunController()
diff --git a/testing/legion/examples/subprocess/client.isolate b/testing/legion/examples/subprocess/task.isolate
similarity index 85%
rename from testing/legion/examples/subprocess/client.isolate
rename to testing/legion/examples/subprocess/task.isolate
index 611562c..534275d 100644
--- a/testing/legion/examples/subprocess/client.isolate
+++ b/testing/legion/examples/subprocess/task.isolate
@@ -11,10 +11,10 @@
       'variables': {
         'command': [
           'python',
-          '../../client_controller.py',
+          '../../run_task.py',
         ],
         'files': [
-          'client.isolate'
+          'task.isolate'
         ],
       },
     }],
diff --git a/testing/legion/host_controller.py b/testing/legion/host_controller.py
deleted file mode 100644
index dadcba4..0000000
--- a/testing/legion/host_controller.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Defines the host controller base library.
-
-This module is the basis on which host controllers are built and executed.
-"""
-
-import logging
-import sys
-
-#pylint: disable=relative-import
-import client_lib
-import common_lib
-import discovery_server
-
-
-class HostController(object):
-  """The base host controller class."""
-
-  def __init__(self):
-    self._discovery_server = discovery_server.DiscoveryServer()
-
-  def SetUp(self):
-    """Setup method used by the subclass."""
-    pass
-
-  def Task(self):
-    """Main task method used by the subclass."""
-    pass
-
-  def TearDown(self):
-    """Teardown method used by the subclass."""
-    pass
-
-  def NewClient(self, *args, **kwargs):
-    controller = client_lib.ClientController(*args, **kwargs)
-    self._discovery_server.RegisterClientCallback(
-        controller.otp, controller.OnConnect)
-    return controller
-
-  def RunController(self):
-    """Main entry point for the controller."""
-    print ' '.join(sys.argv)
-    common_lib.InitLogging()
-    self._discovery_server.Start()
-
-    error = None
-    tb = None
-    try:
-      self.SetUp()
-      self.Task()
-    except Exception as e:
-      # Defer raising exceptions until after TearDown and _TearDown are called.
-      error = e
-      tb = sys.exc_info()[-1]
-    try:
-      self.TearDown()
-    except Exception as e:
-      # Defer raising exceptions until after _TearDown is called.
-      # Note that an error raised here will obscure any errors raised
-      # previously.
-      error = e
-      tb = sys.exc_info()[-1]
-
-    self._discovery_server.Shutdown()
-    client_lib.ClientController.ReleaseAllControllers()
-    if error:
-      raise error, None, tb  #pylint: disable=raising-bad-type
diff --git a/testing/legion/legion.isolate b/testing/legion/legion.isolate
index 463764d..774b27a 100644
--- a/testing/legion/legion.isolate
+++ b/testing/legion/legion.isolate
@@ -6,14 +6,14 @@
   'variables': {
     'files': [
       '__init__.py',
-      'client_controller.py',
-      'client_lib.py',
-      'client_rpc_methods.py',
-      'client_rpc_server.py',
       'common_lib.py',
-      'discovery_server.py',
-      'host_controller.py',
       'legion.isolate',
+      'rpc_methods.py',
+      'rpc_server.py',
+      'run_task.py',
+      'task_controller.py',
+      'task_registration_server.py',
+      'test_controller.py',
       '../../tools/swarming_client/',
     ],
   },
diff --git a/testing/legion/client_rpc_methods.py b/testing/legion/rpc_methods.py
similarity index 98%
rename from testing/legion/client_rpc_methods.py
rename to testing/legion/rpc_methods.py
index e43a7d8..7f17e23 100644
--- a/testing/legion/client_rpc_methods.py
+++ b/testing/legion/rpc_methods.py
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Defines the client RPC methods."""
+"""Defines the task RPC methods."""
 
 import os
 import sys
diff --git a/testing/legion/client_rpc_server.py b/testing/legion/rpc_server.py
similarity index 93%
rename from testing/legion/client_rpc_server.py
rename to testing/legion/rpc_server.py
index 7a5f565..43b4317 100644
--- a/testing/legion/client_rpc_server.py
+++ b/testing/legion/rpc_server.py
@@ -2,10 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""The client RPC server code.
+"""The task RPC server code.
 
 This server is an XML-RPC server which serves code from
-client_rpc_methods.RPCMethods.
+rpc_methods.RPCMethods.
 
 This server will run until shutdown is called on the server object. This can
 be achieved in 2 ways:
@@ -22,8 +22,8 @@
 import SocketServer
 
 #pylint: disable=relative-import
-import client_rpc_methods
 import common_lib
+import rpc_methods
 
 
 class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
@@ -33,10 +33,10 @@
   """
 
   def do_POST(self):
-    """Verifies the client is authorized to perform RPCs."""
+    """Verifies the task is authorized to perform RPCs."""
     if self.client_address[0] != self.server.authorized_address:
       logging.error('Received unauthorized RPC request from %s',
-                    self.client_address[0])
+                    self.task_address[0])
       self.send_response(403)
       response = 'Forbidden'
       self.send_header('Content-type', 'text/plain')
@@ -60,7 +60,7 @@
 
     self.authorized_address = authorized_address
     self.idle_timeout_secs = idle_timeout_secs
-    self.register_instance(client_rpc_methods.RPCMethods(self))
+    self.register_instance(rpc_methods.RPCMethods(self))
 
     self._shutdown_requested_event = threading.Event()
     self._rpc_received_event = threading.Event()
diff --git a/testing/legion/client_controller.py b/testing/legion/run_task.py
similarity index 61%
rename from testing/legion/client_controller.py
rename to testing/legion/run_task.py
index dd80c29..6a55073 100755
--- a/testing/legion/client_controller.py
+++ b/testing/legion/run_task.py
@@ -3,11 +3,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""The main client_controller code.
-
-This code is the main entry point for the client machines and handles
-registering with the host server and running the local RPC server.
-"""
+"""The main task entrypoint."""
 
 import argparse
 import logging
@@ -16,32 +12,32 @@
 import time
 
 #pylint: disable=relative-import
-import client_rpc_server
 import common_lib
+import rpc_server
 
 
 def main():
   print ' '.join(sys.argv)
   common_lib.InitLogging()
-  logging.info('Client controller starting')
+  logging.info('Task starting')
 
   parser = argparse.ArgumentParser()
   parser.add_argument('--otp',
                       help='One time token used to authenticate with the host')
-  parser.add_argument('--host',
-                      help='The ip address of the host')
+  parser.add_argument('--controller',
+                      help='The ip address of the controller machine')
   parser.add_argument('--idle-timeout', type=int,
                       default=common_lib.DEFAULT_TIMEOUT_SECS,
                       help='The idle timeout for the rpc server in seconds')
   args, _ = parser.parse_known_args()
 
   logging.info(
-      'Registering with discovery server at %s using OTP %s', args.host,
-      args.otp)
-  server = common_lib.ConnectToServer(args.host).RegisterClient(
+      'Registering with registration server at %s using OTP "%s"',
+      args.controller, args.otp)
+  server = common_lib.ConnectToServer(args.controller).RegisterTask(
       args.otp, common_lib.MY_IP)
 
-  server = client_rpc_server.RPCServer(args.host, args.idle_timeout)
+  server = rpc_server.RPCServer(args.controller, args.idle_timeout)
 
   server.serve_forever()
   return 0
diff --git a/testing/legion/client_lib.py b/testing/legion/task_controller.py
similarity index 78%
rename from testing/legion/client_lib.py
rename to testing/legion/task_controller.py
index 4656cac..e0812b4 100644
--- a/testing/legion/client_lib.py
+++ b/testing/legion/task_controller.py
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Defines the client library."""
+"""Defines the task controller library."""
 
 import argparse
 import datetime
@@ -30,11 +30,24 @@
   pass
 
 
-class ClientController(object):
-  """Creates, configures, and controls a client machine."""
+class TaskController(object):
+  """Provisions, configures, and controls a task machine.
 
-  _client_count = 0
-  _controllers = []
+  This class is an abstraction of a physical task machine. It provides an
+  end to end API for controlling a task machine. Operations on the task machine
+  are performed using the instance's "rpc" property. A simple end to end
+  scenario is as follows:
+
+  task = TaskController(...)
+  task.Create()
+  task.WaitForConnection()
+  proc = task.rpc.subprocess.Popen(['ls'])
+  print task.rpc.subprocess.GetStdout(proc)
+  task.Release()
+  """
+
+  _task_count = 0
+  _tasks = []
 
   def __init__(self, isolate_file, config_vars, dimensions, priority=100,
                idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
@@ -42,10 +55,10 @@
                verbosity='ERROR', name=None):
     assert isinstance(config_vars, dict)
     assert isinstance(dimensions, dict)
-    type(self)._controllers.append(self)
-    type(self)._client_count += 1
+    type(self)._tasks.append(self)
+    type(self)._task_count += 1
     self.verbosity = verbosity
-    self._name = name or 'Client%d' % type(self)._client_count
+    self._name = name or 'Task%d' % type(self)._task_count
     self._priority = priority
     self._isolate_file = isolate_file
     self._isolated_file = isolate_file + 'd'
@@ -61,14 +74,14 @@
     parser = argparse.ArgumentParser()
     parser.add_argument('--isolate-server')
     parser.add_argument('--swarming-server')
-    parser.add_argument('--client-connection-timeout-secs',
+    parser.add_argument('--task-connection-timeout-secs',
                         default=common_lib.DEFAULT_TIMEOUT_SECS)
     args, _ = parser.parse_known_args()
 
     self._isolate_server = args.isolate_server
     self._swarming_server = args.swarming_server
     self._connection_timeout_secs = (connection_timeout_secs or
-                                    args.client_connection_timeout_secs)
+                                    args.task_connection_timeout_secs)
 
   @property
   def name(self):
@@ -107,31 +120,31 @@
     self._verbosity = level  #pylint: disable=attribute-defined-outside-init
 
   @classmethod
-  def ReleaseAllControllers(cls):
-    for controller in cls._controllers:
-      controller.Release()
+  def ReleaseAllTasks(cls):
+    for task in cls._tasks:
+      task.Release()
 
   def _CreateOTP(self):
     """Creates the OTP."""
-    host_name = socket.gethostname()
+    controller_name = socket.gethostname()
     test_name = os.path.basename(sys.argv[0])
     creation_time = datetime.datetime.utcnow()
-    otp = 'client:%s-host:%s-test:%s-creation:%s' % (
-        self._name, host_name, test_name, creation_time)
+    otp = 'task:%s controller:%s test:%s creation:%s' % (
+        self._name, controller_name, test_name, creation_time)
     return otp
 
   def Create(self):
-    """Creates the client machine."""
+    """Creates the task machine."""
     logging.info('Creating %s', self.name)
     self._connect_event.clear()
     self._ExecuteIsolate()
     self._ExecuteSwarming()
 
   def WaitForConnection(self):
-    """Waits for the client machine to connect.
+    """Waits for the task machine to connect.
 
     Raises:
-      ConnectionTimeoutError if the client doesn't connect in time.
+      ConnectionTimeoutError if the task doesn't connect in time.
     """
     logging.info('Waiting for %s to connect with a timeout of %d seconds',
                  self._name, self._connection_timeout_secs)
@@ -140,7 +153,7 @@
       raise ConnectionTimeoutError('%s failed to connect' % self.name)
 
   def Release(self):
-    """Quits the client's RPC server so it can release the machine."""
+    """Quits the task's RPC server so it can release the machine."""
     if self._rpc is not None and self._connected:
       logging.info('Releasing %s', self._name)
       try:
@@ -186,7 +199,7 @@
 
     cmd.extend([
         '--',
-        '--host', common_lib.MY_IP,
+        '--controller', common_lib.MY_IP,
         '--otp', self._otp,
         '--verbosity', self._verbosity,
         '--idle-timeout', str(self._idle_timeout_secs),
@@ -203,7 +216,7 @@
       raise Error(stderr)
 
   def OnConnect(self, ip_address):
-    """Receives client ip address on connection."""
+    """Receives task ip address on connection."""
     self._ip_address = ip_address
     self._connected = True
     self._rpc = common_lib.ConnectToServer(self._ip_address)
diff --git a/testing/legion/task_registration_server.py b/testing/legion/task_registration_server.py
new file mode 100644
index 0000000..52ba727
--- /dev/null
+++ b/testing/legion/task_registration_server.py
@@ -0,0 +1,55 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""The registration server used to register tasks.
+
+The registration server is started by the test controller and allows the tasks
+to register themselves when they start. Authentication of the tasks controllers
+is based on an OTP passed to the run_task binary on startup.
+"""
+
+import logging
+import threading
+import xmlrpclib
+import SimpleXMLRPCServer
+
+#pylint: disable=relative-import
+import common_lib
+
+
+class TaskRegistrationServer(object):
+  """Discovery server run on the host."""
+
+  def __init__(self):
+    self._expected_tasks = {}
+    self._rpc_server = None
+    self._thread = None
+
+  def _RegisterTaskRPC(self, otp, ip):
+    """The RPC used by a task to register with the registration server."""
+    assert otp in self._expected_tasks
+    cb = self._expected_tasks.pop(otp)
+    cb(ip)
+
+  def RegisterTaskCallback(self, otp, callback):
+    """Registers a callback associated with an OTP."""
+    assert callable(callback)
+    self._expected_tasks[otp] = callback
+
+  def Start(self):
+    """Starts the registration server."""
+    logging.debug('Starting task registration server')
+    self._rpc_server = SimpleXMLRPCServer.SimpleXMLRPCServer(
+        (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT),
+        allow_none=True, logRequests=False)
+    self._rpc_server.register_function(
+        self._RegisterTaskRPC, 'RegisterTask')
+    self._thread = threading.Thread(target=self._rpc_server.serve_forever)
+    self._thread.start()
+
+  def Shutdown(self):
+    """Shuts the discovery server down."""
+    if self._thread and self._thread.is_alive():
+      logging.debug('Shutting down task registration server')
+      self._rpc_server.shutdown()
diff --git a/testing/legion/test_controller.py b/testing/legion/test_controller.py
new file mode 100644
index 0000000..2703fad
--- /dev/null
+++ b/testing/legion/test_controller.py
@@ -0,0 +1,69 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Defines the test controller base library.
+
+This module is the basis on which test controllers are built and executed.
+"""
+
+import logging
+import sys
+
+#pylint: disable=relative-import
+import common_lib
+import task_controller
+import task_registration_server
+
+
+class TestController(object):
+  """The base test controller class."""
+
+  def __init__(self):
+    self._registration_server = (
+        task_registration_server.TaskRegistrationServer())
+
+  def SetUp(self):
+    """Setup method used by the subclass."""
+    pass
+
+  def RunTest(self):
+    """Main test method used by the subclass."""
+    raise NotImplementedError()
+
+  def TearDown(self):
+    """Teardown method used by the subclass."""
+    pass
+
+  def CreateNewTask(self, *args, **kwargs):
+    task = task_controller.TaskController(*args, **kwargs)
+    self._registration_server.RegisterTaskCallback(
+        task.otp, task.OnConnect)
+    return task
+
+  def RunController(self):
+    """Main entry point for the controller."""
+    print ' '.join(sys.argv)
+    common_lib.InitLogging()
+    self._registration_server.Start()
+
+    error = None
+    tb = None
+    try:
+      self.SetUp()
+      self.RunTest()
+    except Exception as e:
+      # Defer raising exceptions until after TearDown is called.
+      error = e
+      tb = sys.exc_info()[-1]
+    try:
+      self.TearDown()
+    except Exception as e:
+      if not tb:
+        error = e
+        tb = sys.exc_info()[-1]
+
+    self._registration_server.Shutdown()
+    task_controller.TaskController.ReleaseAllTasks()
+    if error:
+      raise error, None, tb  #pylint: disable=raising-bad-type
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index d245868..e147754 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -28,9 +28,9 @@
 if (is_win) {
   import("//third_party/yasm/yasm_assemble.gni")
   yasm_assemble("boringssl_asm") {
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       sources = gypi_values.boringssl_win_x86_64_sources
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       sources = gypi_values.boringssl_win_x86_sources
     }
   }
@@ -52,7 +52,13 @@
   }
 
   configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [ "//build/config/compiler:no_chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+
+    # TODO(davidben): Fix size_t truncations in BoringSSL.
+    # https://crbug.com/429039
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   # Also gets the include dirs from :openssl_config
   include_dirs = [
@@ -64,13 +70,7 @@
     "src/crypto",
   ]
 
-  if (is_win) {
-    # TODO(davidben): Fix size_t truncations in BoringSSL.
-    # https://crbug.com/429039
-    cflags += [ "/wd4267" ]
-  }
-
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     if (is_mac) {
       sources += gypi_values.boringssl_mac_x86_64_sources
     } else if (is_linux || is_android) {
@@ -80,7 +80,7 @@
     } else {
       defines += [ "OPENSSL_NO_ASM" ]
     }
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     if (is_mac) {
       sources += gypi_values.boringssl_mac_x86_sources
     } else if (is_linux || is_android) {
@@ -90,9 +90,9 @@
     } else {
       defines += [ "OPENSSL_NO_ASM" ]
     }
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     sources += gypi_values.boringssl_linux_arm_sources
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     sources += gypi_values.boringssl_linux_aarch64_sources
   } else {
     defines += [ "OPENSSL_NO_ASM" ]
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index 000a9cc..71213fd 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -28,7 +28,7 @@
   } else {
     use_system_harfbuzz = false
   }
-  if (is_linux && cpu_arch == "arm" && !is_chromeos) {
+  if (is_linux && current_cpu == "arm" && !is_chromeos) {
     # Override use_system_harfbuzz for ARM cross compiling so system
     # harfbuzz is not used because the corresponding package is not
     # available.
@@ -161,10 +161,7 @@
       cflags += [ "-Wno-unused-value" ]
     }
     if (is_win) {
-      cflags += [
-        "/wd4267",  # size_t to 'type' converion.
-        "/wd4334",  # Result of 32-bit shift implicitly converted to 64 bits.
-      ]
+      cflags += [ "/wd4334" ]  # Result of 32-bit shift implicitly converted to 64 bits.
     }
     if (is_mac) {
       sources += [
diff --git a/third_party/jstemplate/README.chromium b/third_party/jstemplate/README.chromium
index 6fd6f26..14b92f3 100644
--- a/third_party/jstemplate/README.chromium
+++ b/third_party/jstemplate/README.chromium
@@ -1,6 +1,8 @@
 Name: google-jstemplate
 URL: http://code.google.com/p/google-jstemplate/
 License: Apache 2.0
+Security Critical: yes
+Version: unknown
 
 "Template processing that is more suitable for the specific development-time
 and runtime requirements of AJAX based web applications.
@@ -16,3 +18,7 @@
 
 jstemplate_compiled.js is the output after passing the code through
 compile.sh.
+
+Local modifications:
+Changed JSDoc annotations and subtle code changes to make it compile with modern
+versions of Closure Compiler. TODO(dbeam): upstream to google code project.
diff --git a/third_party/jstemplate/jsevalcontext.js b/third_party/jstemplate/jsevalcontext.js
index 52bbc62..f958a1e 100644
--- a/third_party/jstemplate/jsevalcontext.js
+++ b/third_party/jstemplate/jsevalcontext.js
@@ -243,7 +243,7 @@
  *
  * @param {Object} data The new context object.
  *
- * @param {number} index Position of the new context when multiply
+ * @param {number|string} index Position of the new context when multiply
  * instantiated. (See implementation of jstSelect().)
  * 
  * @param {number} count The total number of contexts that were multiply
diff --git a/third_party/jstemplate/jstemplate.js b/third_party/jstemplate/jstemplate.js
index b4c154f..449a31c 100644
--- a/third_party/jstemplate/jstemplate.js
+++ b/third_party/jstemplate/jstemplate.js
@@ -552,9 +552,6 @@
  * @param {Element} template The currently processed node of the template.
  *
  * @param {Function} select The javascript expression to evaluate.
- *
- * @notypecheck FIXME(hmitchell): See OCL6434950. instance and value need
- * type checks.
  */
 JstProcessor.prototype.jstSelect_ = function(context, template, select) {
   var me = this;
@@ -586,6 +583,7 @@
   var multipleEmpty = (multiple && count == 0);
 
   if (multiple) {
+    value = /** @type Array */(value);
     if (multipleEmpty) {
       // For an empty array, keep the first template instance and mark
       // it last. Remove all other template instances.
@@ -938,7 +936,7 @@
  * @param {Array} values The current input context, the array of
  * values of which the template node will render one instance.
  *
- * @param {number} index The index of this template node in values.
+ * @param {number|string} index The index of this template node in values.
  */
 function jstSetInstance(template, values, index) {
   if (index == jsLength(values) - 1) {
diff --git a/third_party/libpng/BUILD.gn b/third_party/libpng/BUILD.gn
index 488ef43..787d4fa 100644
--- a/third_party/libpng/BUILD.gn
+++ b/third_party/libpng/BUILD.gn
@@ -52,12 +52,8 @@
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): http://crbug.com/167187
-
-    if (component_mode == "shared_library") {
-      defines = [ "PNG_BUILD_DLL" ]
-    }
+  if (is_win && is_component_build) {
+    defines = [ "PNG_BUILD_DLL" ]
   }
 
   public_configs = [ ":libpng_config" ]
diff --git a/third_party/ots/BUILD.gn b/third_party/ots/BUILD.gn
new file mode 100644
index 0000000..513dce6
--- /dev/null
+++ b/third_party/ots/BUILD.gn
@@ -0,0 +1,90 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("ots_config") {
+  include_dirs = [ "include" ]
+}
+
+source_set("ots") {
+  sources = [
+    "include/ots-memory-stream.h",
+    "include/opentype-sanitiser.h",
+    "src/cff.cc",
+    "src/cff.h",
+    "src/cff_type2_charstring.cc",
+    "src/cff_type2_charstring.h",
+    "src/cmap.cc",
+    "src/cmap.h",
+    "src/cvt.cc",
+    "src/cvt.h",
+    "src/fpgm.cc",
+    "src/fpgm.h",
+    "src/gasp.cc",
+    "src/gasp.h",
+    "src/gdef.cc",
+    "src/gdef.h",
+    "src/glyf.cc",
+    "src/glyf.h",
+    "src/gpos.cc",
+    "src/gpos.h",
+    "src/gsub.cc",
+    "src/gsub.h",
+    "src/hdmx.cc",
+    "src/hdmx.h",
+    "src/head.cc",
+    "src/head.h",
+    "src/hhea.cc",
+    "src/hhea.h",
+    "src/hmtx.cc",
+    "src/hmtx.h",
+    "src/kern.cc",
+    "src/kern.h",
+    "src/layout.cc",
+    "src/layout.h",
+    "src/loca.cc",
+    "src/loca.h",
+    "src/ltsh.cc",
+    "src/ltsh.h",
+    "src/maxp.cc",
+    "src/maxp.h",
+    "src/math.cc",
+    "src/math_.h",
+    "src/metrics.cc",
+    "src/metrics.h",
+    "src/name.cc",
+    "src/name.h",
+    "src/os2.cc",
+    "src/os2.h",
+    "src/ots.cc",
+    "src/ots.h",
+    "src/post.cc",
+    "src/post.h",
+    "src/prep.cc",
+    "src/prep.h",
+    "src/vdmx.cc",
+    "src/vdmx.h",
+    "src/vhea.cc",
+    "src/vhea.h",
+    "src/vmtx.cc",
+    "src/vmtx.h",
+    "src/vorg.cc",
+    "src/vorg.h",
+    "src/woff2.cc",
+    "src/woff2.h",
+  ]
+
+  direct_dependent_configs = [ ":ots_config" ]
+
+  deps = [
+    "//third_party/brotli",
+    "//third_party/zlib",
+  ]
+
+  if (is_win) {
+    cflags = [
+      "/wd4267",  # Conversion from size_t to 'type'.
+      "/wd4334",  # 32-bit shift implicitly converted to 64-bits.
+    ]
+  }
+}
diff --git a/third_party/ots/INSTALL b/third_party/ots/INSTALL
new file mode 100644
index 0000000..dbb1d94
--- /dev/null
+++ b/third_party/ots/INSTALL
@@ -0,0 +1,38 @@
+How to build (using gyp):
+
+  (Note: test programs which require gtest can't build with gyp for now)
+
+  1. If you are building OTS on Windows, download both the source
+     code and compiled driver for zlib from http://www.zlib.net/
+     and put them in third_party/zlib.
+
+  2. If you are building from cloned Git repository, make sure to update the
+     submodules as well:
+
+     $ git submodule init
+     $ git submodule update
+
+  3. Run gyp_ots
+
+    $ python gyp_ots
+
+    This will fetch gyp and generate build files. By default, following
+    files will be generated:
+      - MSVS solution file on Windows
+      - Xcode project file on Mac
+      - Makefile on Linux
+
+    If you want to generate Makefile on Mac, you can use -f option:
+
+    $ python gyp_ots -f make
+
+  4. Build OTS
+
+    Using MSVS:
+      Open ots-standalone.sln and build the solution.
+
+    Using Xcode:
+      $ xcodebuild -target ots-standalone.xcodeproj -target all
+
+    Using Makefile:
+      $ make
diff --git a/third_party/ots/LICENSE b/third_party/ots/LICENSE
new file mode 100644
index 0000000..a7531cf
--- /dev/null
+++ b/third_party/ots/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/ots/OWNERS b/third_party/ots/OWNERS
new file mode 100644
index 0000000..96e5d87
--- /dev/null
+++ b/third_party/ots/OWNERS
@@ -0,0 +1,4 @@
+bashi@chromium.org
+behdad@chromium.org
+behdad@google.com
+jshin@chromium.org
diff --git a/third_party/ots/README b/third_party/ots/README
new file mode 100644
index 0000000..8dc1ebb
--- /dev/null
+++ b/third_party/ots/README
@@ -0,0 +1,21 @@
+Sanitiser for OpenType
+----------------------
+
+(Idea from Alex Russell)
+
+The CSS font-face property[1] is great for web typography. Having to use images
+in order to get the correct typeface is a great sadness; one should be able to
+use vectors.
+
+However, the TrueType renderers on many platforms have never been part of the
+attack surface before and putting them on the front line is a scary proposition.
+Esp on platforms like Windows where it's a closed-source blob running with high
+privilege.
+
+Thus, the OpenType Sanitiser (OTS) is designed to parse and serialise OpenType
+files, validating them and sanitising them as it goes.
+
+
+See INSTALL for build instructions.
+
+[1] http://www.w3.org/TR/CSS2/fonts.html#font-descriptions
diff --git a/third_party/ots/README.chromium b/third_party/ots/README.chromium
new file mode 100644
index 0000000..c828813
--- /dev/null
+++ b/third_party/ots/README.chromium
@@ -0,0 +1,5 @@
+Name: OTS
+URL: https://github.com/khaledhosny/ots.git
+Version: ea88f974e00e7fe0b4fbfe8d0adad8cfedf49c57
+Security Critical: yes
+License: BSD
diff --git a/third_party/ots/docs/DesignDoc.md b/third_party/ots/docs/DesignDoc.md
new file mode 100644
index 0000000..ffcc035
--- /dev/null
+++ b/third_party/ots/docs/DesignDoc.md
@@ -0,0 +1,136 @@
+What's OTS?
+===========
+
+Sanitiser for OpenType (OTS) is a small library which parses OpenType files
+(usually from `@font-face`) and attempts to validate and sanitise them. This
+library is primarily intended to be used with Chromium. We hope this reduces
+the attack surface of the system font libraries.
+
+What the sanitiser does is as follows:
+
+1. Parses an original font. If the parsing fails, OTS rejects the original
+   font.
+2. Validates the parsed data structure. If the validation fails, it rejects the
+   original font as well.
+3. Creates a new font on memory by serializing the data structure, and we call
+   this "transcoding".
+
+By transcoding fonts in this way, it is ensured that:
+
+1. All information in an original font that OTS doesn't know or can't parse is
+   dropped from the transcoded font.
+2. All information in the transcoded font is valid (standard compliant).
+   Particularly 'length' and 'offset' values, that are often used as attack
+   vectors, are ensured to be correct.
+
+Supported OpenType tables
+=========================
+
+| Name   | Mandatory table?            | Supported by OTS? | Note   |
+|--------|-----------------------------|-------------------|--------|
+| `sfnt` | Yes                         | Yes               | Overlapped tables are not allowed; it is treated as a fatal parser error.|
+| `maxp` | Yes                         | Yes               |        |
+| `head` | Yes                         | Yes               |        |
+| `hhea` | Yes                         | Yes               |        |
+| `hmtx` | Yes                         | Yes               |        |
+| `name` | Yes                         | Yes               |        |
+| `OS/2` | Yes                         | Yes               |        |
+| `post` | Yes                         | Yes               |        |
+| `cmap` | Yes                         | Partialy          | see below |
+| `glyf` | Yes, for TrueType fonts     | Yes               | TrueType bytecode is supported, but OTS does **not** validate it.|
+| `loca` | Yes, when glyf table exists | Yes               |        |
+| `CFF ` | Yes, for OpenType fonts     | Yes               | OpenType bytecode is also supported, and OTS **does** validate it.|
+| `cvt ` | No                          | Yes               | Though this table is not mandatory, OTS can't drop the table from a transcoded font since it might be referred from other hinting-related tables. Errors on this table should be treated as fatal.|
+| `fpgm` | No                          | Yes               | Ditto. |
+| `prep` | No                          | Yes               | Ditto. |
+| `VDMX` | No                          | Yes               | This table is important for calculating the correct line spacing, at least on Chromium Windows and Chromium Linux.|
+| `hdmx` | No                          | Yes               |        |
+| `gasp` | No                          | Yes               |        |
+| `VORG` | No                          | Yes               |        |
+| `LTSH` | No                          | Yes               |        |
+| `kern` | No                          | Yes               |        |
+| `GDEF` | No                          | Yes               |        |
+| `GSUB` | No                          | Yes               |        |
+| `GPOS` | No                          | Yes               |        |
+| `morx` | No                          | No                |        |
+| `jstf` | No                          | No                |        |
+| `vmtx` | No                          | Yes               |        |
+| `vhea` | No                          | Yes               |        |
+| `EBDT` | No                          | No                | We don't support embedded bitmap strikes.|
+| `EBLC` | No                          | No                | Ditto. |
+| `EBSC` | No                          | No                | Ditto. |
+| `bdat` | No                          | No                | Ditto. |
+| `bhed` | No                          | No                | Ditto. |
+| `bloc` | No                          | No                | Ditto. |
+| `DSIG` | No                          | No                |        |
+| All other tables | -                 | No                |        |
+
+Please note that OTS library does not parse "unsupported" tables. These
+unsupported tables never appear in a transcoded font.
+
+Supported cmap formats
+----------------------
+
+The following 9 formats are supported:
+
+* "MS Unicode" (platform 3 encoding 1 format 4)
+    * BMP
+* "MS UCS-4" (platform 3 encoding 10 format 12)
+* "MS UCS-4 fallback" (platform 3 encoding 10 format 13)
+* "MS Symbol" (platform 3 encoding 0 format 4)
+* "Mac Roman" (platform 1 encoding 0 format 0)
+    * 1-0-0 format is supported while 1-0-6 is not.
+* "Unicode default" format (platform 0 encoding 0 format 4)
+    * treated as 3-1-4 format
+* "Unicode 1.1" format (platform 0 encoding 1 format 4)
+    * ditto
+* "Unicode 2.0+" format (platform 0 encoding 3 format 4)
+* "Unicode UCS-4" format (platform 0 encoding 4 format 12)
+    * treated as 3-10-12 format
+* Unicode Variation Sequences (platform 0 encoding 5 format 14)
+
+All other types of subtables are not supported and do not appear in transcoded fonts.
+
+Validation strategies
+=====================
+
+With regards to 8 mandatory tables, glyph-related tables (`glyf`, `loca` and `CFF`),
+and hinting-related tables (`cvt`, `prep`, and `fpgm`):
+
+* If OTS finds table-length, table-offset, or table-alignment errors, in other
+  words it cannot continue parsing, OTS treats the error as fatal.
+* If OTS finds simple value error which could be automatically fixed (e.g.,
+  font weight is greater than 900 - that's undefined), and if the error is
+  considered common among non-malicious fonts, OTS rewrites the value and
+  continues transcoding.
+* If OTS finds a value error which is hard to fix (e.g., values which should be
+  sorted are left unsorted), OTS treats the error as fatal.
+
+With regards to optional tables (`VORG`, `gasp`, `hdmx`, `LTSH`, and `VDMX`):
+
+* If OTS finds table-length, table-offset, or table-alignment errors, OTS
+  treats the error as fatal.
+* If OTS finds other errors, it simply drops the table from a transcoded font.
+
+Files
+=====
+
+* include/opentype-sanitiser.h
+    * Declaration for the public API, `ots::Process()`.
+    * Definition of the `OTSStream` interface, a write-only memory stream.
+* include/ots-memory-stream.h
+    * Definition of the `MemoryStream` class which implements the `OTSStream`
+      interface above.
+* src/ots.h
+    * Debug macros.
+    * Definition of a `Buffer` class which is a read-only memory stream.
+* src/ots.cc
+    * Definition of the `ots::Process()` function.
+    * `sfnt` table parser.
+* test/\*.cc
+    * test tools. see test/README for details.
+
+Known issues
+============
+
+Please check the [issues](https://github.com/khaledhosny/ots/issues) page.
diff --git a/third_party/ots/docs/HowToTest.md b/third_party/ots/docs/HowToTest.md
new file mode 100644
index 0000000..a18ee34
--- /dev/null
+++ b/third_party/ots/docs/HowToTest.md
@@ -0,0 +1,68 @@
+Prerequisites
+=============
+
+You can use your Ubuntu box (>= 8.04. 9.10 is recommended) to test OTS library.
+
+First, install TrueType and OpenType fonts to the Ubuntu box as many as
+possible.
+
+    % sudo apt-get install ttf-.*[^0]$
+
+Then, put malicious TrueType fonts on `~/malicious/`. For details, please check
+http://code.google.com/p/chromium/issues/detail?id=27139#c2. Currently access
+to the issue is limited to chromium-security team members for security reasons.
+
+    % cd
+    % tar xjf ~/ttf-testsuite.tar.bz2
+
+Test
+====
+
+In order to verify that:
+
+1. OTS does not reject these unmalicious fonts.
+2. and transcoded fonts OTS generates can be loaded by a system font renderer (FreeType2).
+
+Run `test_unmalicious_fonts.sh` script:
+
+    % cd /path/to/ots/tests
+    % ./test_unmalicious_fonts.sh
+    ...............................................  (verify that no FAIL: is displayed)
+
+Then in order to verify that:
+
+1. OTS can reject malicious fonts
+2. or transcoded fonts generated by OTS do not crash a system font renderer (FreeType2).
+
+Run `test_malicious_fonts.sh` script:
+
+    % cd /path/to/ots/tests
+    % ./test_malicious_fonts.sh
+    ...............................................  (verify that no FAIL: is displayed)
+
+Command line tools
+==================
+
+We have some command line tools for tests. To build them:
+
+- On Linux:
+
+        % gyp --depth=. -f make ots-standalone.gyp
+        % make
+        (tool is located at build/Default directory)
+
+- On Windows (VC++ is needed):
+
+        % gyp --depth=. -f msvs ots-standalone.gyp
+        % devenv.exe /build Default ots-standalone.sln /project idempotent.vcproj
+        (tool is located at Default directory)
+
+- On Mac (XCode is needed):
+
+        % gyp --depth=. -f xcode ots-standalone.gyp
+        % xcodebuild -configuration Default -project ots-standalone.xcodeproj -target All
+        (tool is located at build/Default directory)
+
+You can use `idempotent` tool to check whether a font will be rejected or not.
+You can also use `ot-sanitise` tool to get sanitised font (it is available on
+Linux for now). See README file in the test directory for more details.
diff --git a/third_party/ots/gyp_ots b/third_party/ots/gyp_ots
new file mode 100755
index 0000000..9a4056e
--- /dev/null
+++ b/third_party/ots/gyp_ots
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import sys
+
+_GYP_REVISION = '1344'
+_GYP_FETCH_URL = 'https://gyp.googlecode.com/svn/trunk@' + _GYP_REVISION
+
+def _fetch_gyp():
+  gyp_dir = os.path.join('third_party', 'gyp')
+  if not os.path.exists(gyp_dir):
+    retcode = subprocess.call(['svn', 'checkout', _GYP_FETCH_URL, gyp_dir])
+    if retcode < 0:
+      raise "Couldn't fetch gyp"
+  # TODO(bashi): Check revision, etc
+  sys.path.insert(0, os.path.abspath(os.path.join(gyp_dir, 'pylib')))
+
+def main():
+  script_dir = os.path.abspath(os.path.dirname(__file__))
+  os.chdir(script_dir)
+  _fetch_gyp()
+  import gyp
+
+  args = []
+  args.extend(['--depth', '.'])
+  args.extend(sys.argv[1:])
+  args.append(os.path.join(script_dir, 'ots-standalone.gyp'))
+  sys.exit(gyp.main(args))
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/ots/include/opentype-sanitiser.h b/third_party/ots/include/opentype-sanitiser.h
new file mode 100644
index 0000000..c454f1e
--- /dev/null
+++ b/third_party/ots/include/opentype-sanitiser.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OPENTYPE_SANITISER_H_
+#define OPENTYPE_SANITISER_H_
+
+#if defined(_WIN32)
+#include <stdlib.h>
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#define ntohl(x) _byteswap_ulong (x)
+#define ntohs(x) _byteswap_ushort (x)
+#define htonl(x) _byteswap_ulong (x)
+#define htons(x) _byteswap_ushort (x)
+#else
+#include <arpa/inet.h>
+#include <stdint.h>
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+namespace ots {
+
+// -----------------------------------------------------------------------------
+// This is an interface for an abstract stream class which is used for writing
+// the serialised results out.
+// -----------------------------------------------------------------------------
+class OTSStream {
+ public:
+  OTSStream() {
+    ResetChecksum();
+  }
+
+  virtual ~OTSStream() {}
+
+  // This should be implemented to perform the actual write.
+  virtual bool WriteRaw(const void *data, size_t length) = 0;
+
+  bool Write(const void *data, size_t length) {
+    if (!length) return false;
+
+    const size_t orig_length = length;
+    size_t offset = 0;
+    if (chksum_buffer_offset_) {
+      const size_t l =
+        std::min(length, static_cast<size_t>(4) - chksum_buffer_offset_);
+      std::memcpy(chksum_buffer_ + chksum_buffer_offset_, data, l);
+      chksum_buffer_offset_ += l;
+      offset += l;
+      length -= l;
+    }
+
+    if (chksum_buffer_offset_ == 4) {
+      uint32_t tmp;
+      std::memcpy(&tmp, chksum_buffer_, 4);
+      chksum_ += ntohl(tmp);
+      chksum_buffer_offset_ = 0;
+    }
+
+    while (length >= 4) {
+      uint32_t tmp;
+      std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
+        sizeof(uint32_t));
+      chksum_ += ntohl(tmp);
+      length -= 4;
+      offset += 4;
+    }
+
+    if (length) {
+      if (chksum_buffer_offset_ != 0) return false;  // not reached
+      if (length > 4) return false;  // not reached
+      std::memcpy(chksum_buffer_,
+             reinterpret_cast<const uint8_t*>(data) + offset, length);
+      chksum_buffer_offset_ = length;
+    }
+
+    return WriteRaw(data, orig_length);
+  }
+
+  virtual bool Seek(off_t position) = 0;
+  virtual off_t Tell() const = 0;
+
+  virtual bool Pad(size_t bytes) {
+    static const uint32_t kZero = 0;
+    while (bytes >= 4) {
+      if (!WriteTag(kZero)) return false;
+      bytes -= 4;
+    }
+    while (bytes) {
+      static const uint8_t kZerob = 0;
+      if (!Write(&kZerob, 1)) return false;
+      bytes--;
+    }
+    return true;
+  }
+
+  bool WriteU8(uint8_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteU16(uint16_t v) {
+    v = htons(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteS16(int16_t v) {
+    v = htons(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteU24(uint32_t v) {
+    v = htonl(v);
+    return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
+  }
+
+  bool WriteU32(uint32_t v) {
+    v = htonl(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteS32(int32_t v) {
+    v = htonl(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteR64(uint64_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteTag(uint32_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  void ResetChecksum() {
+    chksum_ = 0;
+    chksum_buffer_offset_ = 0;
+  }
+
+  uint32_t chksum() const {
+    assert(chksum_buffer_offset_ == 0);
+    return chksum_;
+  }
+
+  struct ChecksumState {
+    uint32_t chksum;
+    uint8_t chksum_buffer[4];
+    unsigned chksum_buffer_offset;
+  };
+
+  ChecksumState SaveChecksumState() const {
+    ChecksumState s;
+    s.chksum = chksum_;
+    s.chksum_buffer_offset = chksum_buffer_offset_;
+    std::memcpy(s.chksum_buffer, chksum_buffer_, 4);
+
+    return s;
+  }
+
+  void RestoreChecksum(const ChecksumState &s) {
+    assert(chksum_buffer_offset_ == 0);
+    chksum_ += s.chksum;
+    chksum_buffer_offset_ = s.chksum_buffer_offset;
+    std::memcpy(chksum_buffer_, s.chksum_buffer, 4);
+  }
+
+ protected:
+  uint32_t chksum_;
+  uint8_t chksum_buffer_[4];
+  unsigned chksum_buffer_offset_;
+};
+
+#ifdef __GCC__
+#define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3)))
+#else
+#define MSGFUNC_FMT_ATTR
+#endif
+
+enum TableAction {
+  TABLE_ACTION_DEFAULT,  // Use OTS's default action for that table
+  TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
+  TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
+  TABLE_ACTION_DROP      // Drop the table
+};
+
+class OTSContext {
+  public:
+    OTSContext() {}
+    virtual ~OTSContext() {}
+
+    // Process a given OpenType file and write out a sanitised version
+    //   output: a pointer to an object implementing the OTSStream interface. The
+    //     sanitisied output will be written to this. In the even of a failure,
+    //     partial output may have been written.
+    //   input: the OpenType file
+    //   length: the size, in bytes, of |input|
+    //   context: optional context that holds various OTS settings like user callbacks
+    bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+    // This function will be called when OTS is reporting an error.
+    //   level: the severity of the generated message:
+    //     0: error messages in case OTS fails to sanitize the font.
+    //     1: warning messages about issue OTS fixed in the sanitized font.
+    virtual void Message(int level, const char *format, ...) MSGFUNC_FMT_ATTR {}
+
+    // This function will be called when OTS needs to decide what to do for a
+    // font table.
+    //   tag: table tag as an integer in big-endian byte order, independent of
+    //   platform endianness
+    virtual TableAction GetTableAction(uint32_t tag) { return ots::TABLE_ACTION_DEFAULT; }
+};
+
+// For backward compatibility - remove once Chrome switches over to the new API.
+bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+// For backward compatibility - remove once https://codereview.chromium.org/774253008/
+// is submitted.
+void EnableWOFF2();
+
+}  // namespace ots
+
+#endif  // OPENTYPE_SANITISER_H_
diff --git a/third_party/ots/include/ots-memory-stream.h b/third_party/ots/include/ots-memory-stream.h
new file mode 100644
index 0000000..579da61
--- /dev/null
+++ b/third_party/ots/include/ots-memory-stream.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MEMORY_STREAM_H_
+#define OTS_MEMORY_STREAM_H_
+
+#include <cstring>
+#include <limits>
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+class MemoryStream : public OTSStream {
+ public:
+  MemoryStream(void *ptr, size_t length)
+      : ptr_(ptr), length_(length), off_(0) {
+  }
+
+  virtual bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      return false;
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  virtual bool Seek(off_t position) {
+    if (position < 0) return false;
+    if (static_cast<size_t>(position) > length_) return false;
+    off_ = position;
+    return true;
+  }
+
+  virtual off_t Tell() const {
+    return off_;
+  }
+
+ private:
+  void* const ptr_;
+  size_t length_;
+  off_t off_;
+};
+
+class ExpandingMemoryStream : public OTSStream {
+ public:
+  ExpandingMemoryStream(size_t initial, size_t limit)
+      : length_(initial), limit_(limit), off_(0) {
+    ptr_ = new uint8_t[length_];
+  }
+
+  ~ExpandingMemoryStream() {
+    delete[] static_cast<uint8_t*>(ptr_);
+  }
+
+  void* get() const {
+    return ptr_;
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      if (length_ == limit_)
+        return false;
+      size_t new_length = (length_ + 1) * 2;
+      if (new_length < length_)
+        return false;
+      if (new_length > limit_)
+        new_length = limit_;
+      uint8_t* new_buf = new uint8_t[new_length];
+      std::memcpy(new_buf, ptr_, length_);
+      length_ = new_length;
+      delete[] static_cast<uint8_t*>(ptr_);
+      ptr_ = new_buf;
+      return WriteRaw(data, length);
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  bool Seek(off_t position) {
+    if (position < 0) return false;
+    if (static_cast<size_t>(position) > length_) return false;
+    off_ = position;
+    return true;
+  }
+
+  off_t Tell() const {
+    return off_;
+  }
+
+ private:
+  void* ptr_;
+  size_t length_;
+  const size_t limit_;
+  off_t off_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MEMORY_STREAM_H_
diff --git a/third_party/ots/ots-common.gypi b/third_party/ots/ots-common.gypi
new file mode 100644
index 0000000..9cb539c
--- /dev/null
+++ b/third_party/ots/ots-common.gypi
@@ -0,0 +1,77 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'ots_sources': [
+      'include/ots-memory-stream.h',
+      'include/opentype-sanitiser.h',
+      'src/cff.cc',
+      'src/cff.h',
+      'src/cff_type2_charstring.cc',
+      'src/cff_type2_charstring.h',
+      'src/cmap.cc',
+      'src/cmap.h',
+      'src/cvt.cc',
+      'src/cvt.h',
+      'src/fpgm.cc',
+      'src/fpgm.h',
+      'src/gasp.cc',
+      'src/gasp.h',
+      'src/gdef.cc',
+      'src/gdef.h',
+      'src/glyf.cc',
+      'src/glyf.h',
+      'src/gpos.cc',
+      'src/gpos.h',
+      'src/gsub.cc',
+      'src/gsub.h',
+      'src/hdmx.cc',
+      'src/hdmx.h',
+      'src/head.cc',
+      'src/head.h',
+      'src/hhea.cc',
+      'src/hhea.h',
+      'src/hmtx.cc',
+      'src/hmtx.h',
+      'src/kern.cc',
+      'src/kern.h',
+      'src/layout.cc',
+      'src/layout.h',
+      'src/loca.cc',
+      'src/loca.h',
+      'src/ltsh.cc',
+      'src/ltsh.h',
+      'src/maxp.cc',
+      'src/maxp.h',
+      'src/math.cc',
+      'src/math_.h',
+      'src/metrics.cc',
+      'src/metrics.h',
+      'src/name.cc',
+      'src/name.h',
+      'src/os2.cc',
+      'src/os2.h',
+      'src/ots.cc',
+      'src/ots.h',
+      'src/post.cc',
+      'src/post.h',
+      'src/prep.cc',
+      'src/prep.h',
+      'src/vdmx.cc',
+      'src/vdmx.h',
+      'src/vhea.cc',
+      'src/vhea.h',
+      'src/vmtx.cc',
+      'src/vmtx.h',
+      'src/vorg.cc',
+      'src/vorg.h',
+      'src/woff2.cc',
+      'src/woff2.h',
+    ],
+    'ots_include_dirs': [
+      'include',
+    ],
+  },
+}
diff --git a/third_party/ots/ots-standalone.gyp b/third_party/ots/ots-standalone.gyp
new file mode 100644
index 0000000..a45ec4a
--- /dev/null
+++ b/third_party/ots/ots-standalone.gyp
@@ -0,0 +1,256 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'gcc_cflags': [
+      '-ggdb',
+      '-W',
+      '-Wall',
+      '-Wshadow',
+      '-Wno-unused-parameter',
+      '-fPIE',
+      '-fstack-protector',
+    ],
+    'gcc_ldflags': [
+      '-ggdb',
+      '-fpie',
+      '-Wl,-z,relro',
+      '-Wl,-z,now',
+    ],
+  },
+  'includes': [
+    'ots-common.gypi',
+  ],
+  'target_defaults': {
+    'include_dirs': [
+      '.',
+      'third_party/brotli/dec',
+    ],
+    'conditions': [
+      ['OS=="linux"', {
+        'cflags': [
+          '<@(gcc_cflags)',
+          '-O',
+        ],
+        'ldflags': [
+          '<@(gcc_ldflags)',
+        ],
+        'defines': [
+          '_FORTIFY_SOURCE=2',
+        ],
+        'link_settings': {
+          'libraries': ['-lz'],
+        },
+      }],
+      ['OS=="mac"', {
+        'xcode_settings': {
+          'GCC_DYNAMIC_NO_PIC': 'NO',            # No -mdynamic-no-pic
+          'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES',   # -fvisibility=hidden
+          'OTHER_CFLAGS': [
+            '<@(gcc_cflags)',
+          ],
+        },
+        'link_settings': {
+          'libraries': [
+            '/System/Library/Frameworks/ApplicationServices.framework',
+            '/usr/lib/libz.dylib'
+          ],
+        },
+      }],
+      ['OS=="win"', {
+        'link_settings': {
+          'libraries': [
+            '-lzdll.lib',
+          ],
+        },
+        'msvs_settings': {
+          'VCLinkerTool': {
+            'AdditionalLibraryDirectories': ['third_party/zlib'],
+            'DelayLoadDLLs': ['zlib1.dll'],
+          },
+        },
+        'include_dirs': [
+          'third_party/zlib',
+        ],
+        'defines': [
+          'NOMINMAX', # To suppress max/min macro definition.
+          'WIN32',
+        ],
+      }],
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ots',
+      'type': 'static_library',
+      'sources': [
+        '<@(ots_sources)',
+      ],
+      'dependencies': [
+        'third_party/brotli.gyp:brotli',
+      ],
+      'include_dirs': [
+        '<@(ots_include_dirs)',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<@(ots_include_dirs)',
+        ],
+      },
+    },
+    {
+      'target_name': 'freetype2',
+      'type': 'none',
+      'conditions': [
+        ['OS=="linux"', {
+          'direct_dependent_settings': {
+            'cflags': [
+              '<!(pkg-config freetype2 --cflags)',
+            ],
+            'link_settings': {
+              'libraries': [
+                '<!(pkg-config freetype2 --libs)',
+              ],
+            },
+          },
+        }],
+      ],
+    },
+    {
+      'target_name': 'idempotent',
+      'type': 'executable',
+      'sources': [
+        'test/idempotent.cc',
+      ],
+      'dependencies': [
+        'ots',
+      ],
+      'conditions': [
+        ['OS=="linux"', {
+          'dependencies': [
+            'freetype2',
+          ]
+        }],
+        ['OS=="win"', {
+          'link_settings': {
+            'libraries': [
+              '-lgdi32.lib',
+            ],
+          },
+        }],
+      ],
+    },
+    {
+      'target_name': 'ot-sanitise',
+      'type': 'executable',
+      'sources': [
+        'test/ot-sanitise.cc',
+        'test/file-stream.h',
+      ],
+      'dependencies': [
+        'ots',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS=="linux" or OS=="mac"', {
+      'targets': [
+        {
+          'target_name': 'validator_checker',
+          'type': 'executable',
+          'sources': [
+            'test/validator-checker.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'conditions': [
+            ['OS=="linux"', {
+              'dependencies': [
+                'freetype2',
+              ]
+            }],
+          ],
+        },
+        {
+          'target_name': 'perf',
+          'type': 'executable',
+          'sources': [
+            'test/perf.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+        },
+        {
+          'target_name': 'cff_type2_charstring_test',
+          'type': 'executable',
+          'sources': [
+            'test/cff_type2_charstring_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+        {
+          'target_name': 'layout_common_table_test',
+          'type': 'executable',
+          'sources': [
+            'test/layout_common_table_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+        {
+          'target_name': 'table_dependencies_test',
+          'type': 'executable',
+          'sources': [
+            'test/table_dependencies_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+      ],
+    }],
+    ['OS=="linux"', {
+      'targets': [
+        {
+          'target_name': 'side_by_side',
+          'type': 'executable',
+          'sources': [
+            'test/side-by-side.cc',
+          ],
+          'dependencies': [
+            'freetype2',
+            'ots',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/third_party/ots/ots.gyp b/third_party/ots/ots.gyp
new file mode 100644
index 0000000..288e41c
--- /dev/null
+++ b/third_party/ots/ots.gyp
@@ -0,0 +1,39 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'includes': [
+    'ots-common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'ots',
+      'type': 'static_library',
+      'sources': [
+        '<@(ots_sources)',
+      ],
+      'include_dirs': [
+        '../..',
+        '<@(ots_include_dirs)',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<@(ots_include_dirs)',
+        ],
+      },
+      'dependencies': [
+        '../brotli/brotli.gyp:brotli',
+        '../zlib/zlib.gyp:zlib',
+      ],
+      # TODO(jschuh): http://crbug.com/167187
+      'msvs_disabled_warnings': [
+        4267,
+        4334,
+      ],      
+    },
+  ],
+}
diff --git a/third_party/ots/src/cff.cc b/third_party/ots/src/cff.cc
new file mode 100644
index 0000000..9c7204d
--- /dev/null
+++ b/third_party/ots/src/cff.cc
@@ -0,0 +1,1041 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff.h"
+
+#include <cstring>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "cff_type2_charstring.h"
+
+// CFF - PostScript font program (Compact Font Format) table
+// http://www.microsoft.com/typography/otspec/cff.htm
+// http://www.microsoft.com/typography/otspec/cffspec.htm
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+enum DICT_OPERAND_TYPE {
+  DICT_OPERAND_INTEGER,
+  DICT_OPERAND_REAL,
+  DICT_OPERATOR,
+};
+
+enum DICT_DATA_TYPE {
+  DICT_DATA_TOPLEVEL,
+  DICT_DATA_FDARRAY,
+};
+
+enum FONT_FORMAT {
+  FORMAT_UNKNOWN,
+  FORMAT_CID_KEYED,
+  FORMAT_OTHER,  // Including synthetic fonts
+};
+
+// see Appendix. A
+const size_t kNStdString = 390;
+
+bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
+  if (off_size > 4) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t tmp32 = 0;
+  for (unsigned i = 0; i < off_size; ++i) {
+    uint8_t tmp8 = 0;
+    if (!table->ReadU8(&tmp8)) {
+      return OTS_FAILURE();
+    }
+    tmp32 <<= 8;
+    tmp32 += tmp8;
+  }
+  *offset = tmp32;
+  return true;
+}
+
+bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
+  index->off_size = 0;
+  index->offsets.clear();
+
+  if (!table->ReadU16(&(index->count))) {
+    return OTS_FAILURE();
+  }
+  if (index->count == 0) {
+    // An empty INDEX.
+    index->offset_to_next = table->offset();
+    return true;
+  }
+
+  if (!table->ReadU8(&(index->off_size))) {
+    return OTS_FAILURE();
+  }
+  if ((index->off_size == 0) ||
+      (index->off_size > 4)) {
+    return OTS_FAILURE();
+  }
+
+  const size_t array_size = (index->count + 1) * index->off_size;
+  // less than ((64k + 1) * 4), thus does not overflow.
+  const size_t object_data_offset = table->offset() + array_size;
+  // does not overflow too, since offset() <= 1GB.
+
+  if (object_data_offset >= table->length()) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i <= index->count; ++i) {  // '<=' is not a typo.
+    uint32_t rel_offset = 0;
+    if (!ReadOffset(table, index->off_size, &rel_offset)) {
+      return OTS_FAILURE();
+    }
+    if (rel_offset < 1) {
+      return OTS_FAILURE();
+    }
+    if (i == 0 && rel_offset != 1) {
+      return OTS_FAILURE();
+    }
+
+    if (rel_offset > table->length()) {
+      return OTS_FAILURE();
+    }
+
+    // does not underflow.
+    if (object_data_offset > table->length() - (rel_offset - 1)) {
+      return OTS_FAILURE();
+    }
+
+    index->offsets.push_back(
+        object_data_offset + (rel_offset - 1));  // less than length(), 1GB.
+  }
+
+  for (unsigned i = 1; i < index->offsets.size(); ++i) {
+    // We allow consecutive identical offsets here for zero-length strings.
+    // See http://crbug.com/69341 for more details.
+    if (index->offsets[i] < index->offsets[i - 1]) {
+      return OTS_FAILURE();
+    }
+  }
+
+  index->offset_to_next = index->offsets.back();
+  return true;
+}
+
+bool ParseNameData(
+    ots::Buffer *table, const ots::CFFIndex &index, std::string* out_name) {
+  uint8_t name[256] = {0};
+  if (index.offsets.size() == 0) {  // just in case.
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 1; i < index.offsets.size(); ++i) {
+    const size_t length = index.offsets[i] - index.offsets[i - 1];
+    // font names should be no longer than 127 characters.
+    if (length > 127) {
+      return OTS_FAILURE();
+    }
+
+    table->set_offset(index.offsets[i - 1]);
+    if (!table->Read(name, length)) {
+      return OTS_FAILURE();
+    }
+
+    for (size_t j = 0; j < length; ++j) {
+      // setting the first byte to NUL is allowed.
+      if (j == 0 && name[j] == 0) continue;
+      // non-ASCII characters are not recommended (except the first character).
+      if (name[j] < 33 || name[j] > 126) {
+        return OTS_FAILURE();
+      }
+      // [, ], ... are not allowed.
+      if (std::strchr("[](){}<>/% ", name[j])) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  *out_name = reinterpret_cast<char *>(name);
+  return true;
+}
+
+bool CheckOffset(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
+                 size_t table_length) {
+  if (operand.second != DICT_OPERAND_INTEGER) {
+    return OTS_FAILURE();
+  }
+  if (operand.first >= table_length) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool CheckSid(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
+              size_t sid_max) {
+  if (operand.second != DICT_OPERAND_INTEGER) {
+    return OTS_FAILURE();
+  }
+  if (operand.first > sid_max) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ParseDictDataBcd(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  bool read_decimal_point = false;
+  bool read_e = false;
+
+  uint8_t nibble = 0;
+  size_t count = 0;
+  while (true) {
+    if (!table->ReadU8(&nibble)) {
+      return OTS_FAILURE();
+    }
+    if ((nibble & 0xf0) == 0xf0) {
+      if ((nibble & 0xf) == 0xf) {
+        // TODO(yusukes): would be better to store actual double value,
+        // rather than the dummy integer.
+        operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+                                           DICT_OPERAND_REAL));
+        return true;
+      }
+      return OTS_FAILURE();
+    }
+    if ((nibble & 0x0f) == 0x0f) {
+      operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+                                         DICT_OPERAND_REAL));
+      return true;
+    }
+
+    // check number format
+    uint8_t nibbles[2];
+    nibbles[0] = (nibble & 0xf0) >> 8;
+    nibbles[1] = (nibble & 0x0f);
+    for (unsigned i = 0; i < 2; ++i) {
+      if (nibbles[i] == 0xd) {  // reserved number
+        return OTS_FAILURE();
+      }
+      if ((nibbles[i] == 0xe) &&  // minus
+          ((count > 0) || (i > 0))) {
+        return OTS_FAILURE();  // minus sign should be the first character.
+      }
+      if (nibbles[i] == 0xa) {  // decimal point
+        if (!read_decimal_point) {
+          read_decimal_point = true;
+        } else {
+          return OTS_FAILURE();  // two or more points.
+        }
+      }
+      if ((nibbles[i] == 0xb) ||  // E+
+          (nibbles[i] == 0xc)) {  // E-
+        if (!read_e) {
+          read_e = true;
+        } else {
+          return OTS_FAILURE();  // two or more E's.
+        }
+      }
+    }
+    ++count;
+  }
+}
+
+bool ParseDictDataEscapedOperator(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t op = 0;
+  if (!table->ReadU8(&op)) {
+    return OTS_FAILURE();
+  }
+
+  if ((op <= 14) ||
+      (op >= 17 && op <= 23) ||
+      (op >= 30 && op <= 38)) {
+    operands->push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
+    return true;
+  }
+
+  // reserved area.
+  return OTS_FAILURE();
+}
+
+bool ParseDictDataNumber(
+    ots::Buffer *table, uint8_t b0,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t b1 = 0;
+  uint8_t b2 = 0;
+  uint8_t b3 = 0;
+  uint8_t b4 = 0;
+
+  switch (b0) {
+    case 28:  // shortint
+      if (!table->ReadU8(&b1) ||
+          !table->ReadU8(&b2)) {
+        return OTS_FAILURE();
+      }
+      operands->push_back(std::make_pair(
+          static_cast<uint32_t>((b1 << 8) + b2), DICT_OPERAND_INTEGER));
+      return true;
+
+    case 29:  // longint
+      if (!table->ReadU8(&b1) ||
+          !table->ReadU8(&b2) ||
+          !table->ReadU8(&b3) ||
+          !table->ReadU8(&b4)) {
+        return OTS_FAILURE();
+      }
+      operands->push_back(std::make_pair(
+          static_cast<uint32_t>((b1 << 24) + (b2 << 16) + (b3 << 8) + b4),
+          DICT_OPERAND_INTEGER));
+      return true;
+
+    case 30:  // binary coded decimal
+      return ParseDictDataBcd(table, operands);
+
+    default:
+      break;
+  }
+
+  uint32_t result;
+  if (b0 >=32 && b0 <=246) {
+    result = b0 - 139;
+  } else if (b0 >=247 && b0 <= 250) {
+    if (!table->ReadU8(&b1)) {
+      return OTS_FAILURE();
+    }
+    result = (b0 - 247) * 256 + b1 + 108;
+  } else if (b0 >= 251 && b0 <= 254) {
+    if (!table->ReadU8(&b1)) {
+      return OTS_FAILURE();
+    }
+    result = -(b0 - 251) * 256 + b1 - 108;
+  } else {
+    return OTS_FAILURE();
+  }
+
+  operands->push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
+  return true;
+}
+
+bool ParseDictDataReadNext(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t op = 0;
+  if (!table->ReadU8(&op)) {
+    return OTS_FAILURE();
+  }
+  if (op <= 21) {
+    if (op == 12) {
+      return ParseDictDataEscapedOperator(table, operands);
+    }
+    operands->push_back(std::make_pair(
+        static_cast<uint32_t>(op), DICT_OPERATOR));
+    return true;
+  } else if (op <= 27 || op == 31 || op == 255) {
+    // reserved area.
+    return OTS_FAILURE();
+  }
+
+  return ParseDictDataNumber(table, op, operands);
+}
+
+bool ParsePrivateDictData(
+    const uint8_t *data,
+    size_t table_length, size_t offset, size_t dict_length,
+    DICT_DATA_TYPE type, ots::OpenTypeCFF *out_cff) {
+  ots::Buffer table(data + offset, dict_length);
+  std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+  // Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino
+  // Kaku Gothic Std W8), we create an empty Local Subr here to match the size
+  // of FDArray the size of |local_subrs_per_font|.
+  if (type == DICT_DATA_FDARRAY) {
+    out_cff->local_subrs_per_font.push_back(new ots::CFFIndex);
+  }
+
+  while (table.offset() < dict_length) {
+    if (!ParseDictDataReadNext(&table, &operands)) {
+      return OTS_FAILURE();
+    }
+    if (operands.empty()) {
+      return OTS_FAILURE();
+    }
+    if (operands.size() > 48) {
+      // An operator may be preceded by up to a maximum of 48 operands.
+      return OTS_FAILURE();
+    }
+    if (operands.back().second != DICT_OPERATOR) {
+      continue;
+    }
+
+    // got operator
+    const uint32_t op = operands.back().first;
+    operands.pop_back();
+
+    switch (op) {
+      // array
+      case 6:  // BlueValues
+      case 7:  // OtherBlues
+      case 8:  // FamilyBlues
+      case 9:  // FamilyOtherBlues
+      case (12U << 8) + 12:  // StemSnapH (delta)
+      case (12U << 8) + 13:  // StemSnapV (delta)
+        if (operands.empty()) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // number
+      case 10:  // StdHW
+      case 11:  // StdVW
+      case 20:  // defaultWidthX
+      case 21:  // nominalWidthX
+      case (12U << 8) + 9:   // BlueScale
+      case (12U << 8) + 10:  // BlueShift
+      case (12U << 8) + 11:  // BlueFuzz
+      case (12U << 8) + 17:  // LanguageGroup
+      case (12U << 8) + 18:  // ExpansionFactor
+      case (12U << 8) + 19:  // initialRandomSeed
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // Local Subrs INDEX, offset(self)
+      case 19: {
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first >= 1024 * 1024 * 1024) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first + offset >= table_length) {
+          return OTS_FAILURE();
+        }
+        // parse "16. Local Subrs INDEX"
+        ots::Buffer cff_table(data, table_length);
+        cff_table.set_offset(operands.back().first + offset);
+        ots::CFFIndex *local_subrs_index = NULL;
+        if (type == DICT_DATA_FDARRAY) {
+          if (out_cff->local_subrs_per_font.empty()) {
+            return OTS_FAILURE();  // not reached.
+          }
+          local_subrs_index = out_cff->local_subrs_per_font.back();
+        } else { // type == DICT_DATA_TOPLEVEL
+          if (out_cff->local_subrs) {
+            return OTS_FAILURE();  // two or more local_subrs?
+          }
+          local_subrs_index = new ots::CFFIndex;
+          out_cff->local_subrs = local_subrs_index;
+        }
+        if (!ParseIndex(&cff_table, local_subrs_index)) {
+          return OTS_FAILURE();
+        }
+        break;
+      }
+
+      // boolean
+      case (12U << 8) + 14:  // ForceBold
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first >= 2) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      default:
+        return OTS_FAILURE();
+    }
+    operands.clear();
+  }
+
+  return true;
+}
+
+bool ParseDictData(const uint8_t *data, size_t table_length,
+                   const ots::CFFIndex &index, uint16_t glyphs,
+                   size_t sid_max, DICT_DATA_TYPE type,
+                   ots::OpenTypeCFF *out_cff) {
+  for (unsigned i = 1; i < index.offsets.size(); ++i) {
+    if (type == DICT_DATA_TOPLEVEL) {
+      out_cff->char_strings_array.push_back(new ots::CFFIndex);
+    }
+    size_t dict_length = index.offsets[i] - index.offsets[i - 1];
+    ots::Buffer table(data + index.offsets[i - 1], dict_length);
+
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+    FONT_FORMAT font_format = FORMAT_UNKNOWN;
+    bool have_ros = false;
+    uint16_t charstring_glyphs = 0;
+    size_t charset_offset = 0;
+
+    while (table.offset() < dict_length) {
+      if (!ParseDictDataReadNext(&table, &operands)) {
+        return OTS_FAILURE();
+      }
+      if (operands.empty()) {
+        return OTS_FAILURE();
+      }
+      if (operands.size() > 48) {
+        // An operator may be preceded by up to a maximum of 48 operands.
+        return OTS_FAILURE();
+      }
+      if (operands.back().second != DICT_OPERATOR) continue;
+
+      // got operator
+      const uint32_t op = operands.back().first;
+      operands.pop_back();
+
+      switch (op) {
+        // SID
+        case 0:   // version
+        case 1:   // Notice
+        case 2:   // Copyright
+        case 3:   // FullName
+        case 4:   // FamilyName
+        case (12U << 8) + 0:   // Copyright
+        case (12U << 8) + 21:  // PostScript
+        case (12U << 8) + 22:  // BaseFontName
+        case (12U << 8) + 38:  // FontName
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckSid(operands.back(), sid_max)) {
+            return OTS_FAILURE();
+          }
+          break;
+
+        // array
+        case 5:   // FontBBox
+        case 14:  // XUID
+        case (12U << 8) + 7:   // FontMatrix
+        case (12U << 8) + 23:  // BaseFontBlend (delta)
+          if (operands.empty()) {
+            return OTS_FAILURE();
+          }
+          break;
+
+        // number
+        case 13:  // UniqueID
+        case (12U << 8) + 2:   // ItalicAngle
+        case (12U << 8) + 3:   // UnderlinePosition
+        case (12U << 8) + 4:   // UnderlineThickness
+        case (12U << 8) + 5:   // PaintType
+        case (12U << 8) + 8:   // StrokeWidth
+        case (12U << 8) + 20:  // SyntheticBase
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          break;
+        case (12U << 8) + 31:  // CIDFontVersion
+        case (12U << 8) + 32:  // CIDFontRevision
+        case (12U << 8) + 33:  // CIDFontType
+        case (12U << 8) + 34:  // CIDCount
+        case (12U << 8) + 35:  // UIDBase
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (font_format != FORMAT_CID_KEYED) {
+            return OTS_FAILURE();
+          }
+          break;
+        case (12U << 8) + 6:   // CharstringType
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if(operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first != 2) {
+            // We only support the "Type 2 Charstring Format."
+            // TODO(yusukes): Support Type 1 format? Is that still in use?
+            return OTS_FAILURE();
+          }
+          break;
+
+        // boolean
+        case (12U << 8) + 1:   // isFixedPitch
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first >= 2) {
+            return OTS_FAILURE();
+          }
+          break;
+
+        // offset(0)
+        case 15:  // charset
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first <= 2) {
+            // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
+            break;
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+          if (charset_offset) {
+            return OTS_FAILURE();  // multiple charset tables?
+          }
+          charset_offset = operands.back().first;
+          break;
+
+        case 16: {  // Encoding
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first <= 1) {
+            break;  // predefined encoding, "Standard" or "Expert", is used.
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+
+          // parse sub dictionary INDEX.
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          uint8_t format = 0;
+          if (!cff_table.ReadU8(&format)) {
+            return OTS_FAILURE();
+          }
+          if (format & 0x80) {
+            // supplemental encoding is not supported at the moment.
+            return OTS_FAILURE();
+          }
+          // TODO(yusukes): support & parse supplemental encoding tables.
+          break;
+        }
+
+        case 17: {  // CharStrings
+          if (type != DICT_DATA_TOPLEVEL) {
+            return OTS_FAILURE();
+          }
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+          // parse "14. CharStrings INDEX"
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          ots::CFFIndex *charstring_index = out_cff->char_strings_array.back();
+          if (!ParseIndex(&cff_table, charstring_index)) {
+            return OTS_FAILURE();
+          }
+          if (charstring_index->count < 2) {
+            return OTS_FAILURE();
+          }
+          if (charstring_glyphs) {
+            return OTS_FAILURE();  // multiple charstring tables?
+          }
+          charstring_glyphs = charstring_index->count;
+          if (charstring_glyphs != glyphs) {
+            return OTS_FAILURE();  // CFF and maxp have different number of glyphs?
+          }
+          break;
+        }
+
+        case (12U << 8) + 36: {  // FDArray
+          if (type != DICT_DATA_TOPLEVEL) {
+            return OTS_FAILURE();
+          }
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+
+          // parse sub dictionary INDEX.
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          ots::CFFIndex sub_dict_index;
+          if (!ParseIndex(&cff_table, &sub_dict_index)) {
+            return OTS_FAILURE();
+          }
+          if (!ParseDictData(data, table_length,
+                             sub_dict_index,
+                             glyphs, sid_max, DICT_DATA_FDARRAY,
+                             out_cff)) {
+            return OTS_FAILURE();
+          }
+          if (out_cff->font_dict_length != 0) {
+            return OTS_FAILURE();  // two or more FDArray found.
+          }
+          out_cff->font_dict_length = sub_dict_index.count;
+          break;
+        }
+
+        case (12U << 8) + 37: {  // FDSelect
+          if (type != DICT_DATA_TOPLEVEL) {
+            return OTS_FAILURE();
+          }
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+
+          // parse FDSelect data structure
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          uint8_t format = 0;
+          if (!cff_table.ReadU8(&format)) {
+            return OTS_FAILURE();
+          }
+          if (format == 0) {
+            for (uint16_t j = 0; j < glyphs; ++j) {
+              uint8_t fd_index = 0;
+              if (!cff_table.ReadU8(&fd_index)) {
+                return OTS_FAILURE();
+              }
+              (out_cff->fd_select)[j] = fd_index;
+            }
+          } else if (format == 3) {
+            uint16_t n_ranges = 0;
+            if (!cff_table.ReadU16(&n_ranges)) {
+              return OTS_FAILURE();
+            }
+            if (n_ranges == 0) {
+              return OTS_FAILURE();
+            }
+
+            uint16_t last_gid = 0;
+            uint8_t fd_index = 0;
+            for (unsigned j = 0; j < n_ranges; ++j) {
+              uint16_t first = 0;  // GID
+              if (!cff_table.ReadU16(&first)) {
+                return OTS_FAILURE();
+              }
+
+              // Sanity checks.
+              if ((j == 0) && (first != 0)) {
+                return OTS_FAILURE();
+              }
+              if ((j != 0) && (last_gid >= first)) {
+                return OTS_FAILURE();  // not increasing order.
+              }
+
+              // Copy the mapping to |out_cff->fd_select|.
+              if (j != 0) {
+                for (uint16_t k = last_gid; k < first; ++k) {
+                  if (!out_cff->fd_select.insert(
+                          std::make_pair(k, fd_index)).second) {
+                    return OTS_FAILURE();
+                  }
+                }
+              }
+
+              if (!cff_table.ReadU8(&fd_index)) {
+                return OTS_FAILURE();
+              }
+              last_gid = first;
+              // TODO(yusukes): check GID?
+            }
+            uint16_t sentinel = 0;
+            if (!cff_table.ReadU16(&sentinel)) {
+              return OTS_FAILURE();
+            }
+            if (last_gid >= sentinel) {
+              return OTS_FAILURE();
+            }
+            for (uint16_t k = last_gid; k < sentinel; ++k) {
+              if (!out_cff->fd_select.insert(
+                      std::make_pair(k, fd_index)).second) {
+                return OTS_FAILURE();
+              }
+            }
+          } else {
+            // unknown format
+            return OTS_FAILURE();
+          }
+          break;
+        }
+
+        // Private DICT (2 * number)
+        case 18: {
+          if (operands.size() != 2) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          const uint32_t private_offset = operands.back().first;
+          operands.pop_back();
+          if (operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          const uint32_t private_length = operands.back().first;
+          if (private_offset > table_length) {
+            return OTS_FAILURE();
+          }
+          if (private_length >= table_length) {
+            return OTS_FAILURE();
+          }
+          if (private_length + private_offset > table_length) {
+            return OTS_FAILURE();
+          }
+          // parse "15. Private DICT Data"
+          if (!ParsePrivateDictData(data, table_length,
+                                    private_offset, private_length,
+                                    type, out_cff)) {
+            return OTS_FAILURE();
+          }
+          break;
+        }
+
+        // ROS
+        case (12U << 8) + 30:
+          if (font_format != FORMAT_UNKNOWN) {
+            return OTS_FAILURE();
+          }
+          font_format = FORMAT_CID_KEYED;
+          if (operands.size() != 3) {
+            return OTS_FAILURE();
+          }
+          // check SIDs
+          operands.pop_back();  // ignore the first number.
+          if (!CheckSid(operands.back(), sid_max)) {
+            return OTS_FAILURE();
+          }
+          operands.pop_back();
+          if (!CheckSid(operands.back(), sid_max)) {
+            return OTS_FAILURE();
+          }
+          if (have_ros) {
+            return OTS_FAILURE();  // multiple ROS tables?
+          }
+          have_ros = true;
+          break;
+
+        default:
+          return OTS_FAILURE();
+      }
+      operands.clear();
+
+      if (font_format == FORMAT_UNKNOWN) {
+        font_format = FORMAT_OTHER;
+      }
+    }
+
+    // parse "13. Charsets"
+    if (charset_offset) {
+      ots::Buffer cff_table(data, table_length);
+      cff_table.set_offset(charset_offset);
+      uint8_t format = 0;
+      if (!cff_table.ReadU8(&format)) {
+        return OTS_FAILURE();
+      }
+      switch (format) {
+        case 0:
+          for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
+            uint16_t sid = 0;
+            if (!cff_table.ReadU16(&sid)) {
+              return OTS_FAILURE();
+            }
+            if (!have_ros && (sid > sid_max)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+          }
+          break;
+
+        case 1:
+        case 2: {
+          uint32_t total = 1;  // .notdef is omitted.
+          while (total < glyphs) {
+            uint16_t sid = 0;
+            if (!cff_table.ReadU16(&sid)) {
+              return OTS_FAILURE();
+            }
+            if (!have_ros && (sid > sid_max)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+
+            if (format == 1) {
+              uint8_t left = 0;
+              if (!cff_table.ReadU8(&left)) {
+                return OTS_FAILURE();
+              }
+              total += (left + 1);
+            } else {
+              uint16_t left = 0;
+              if (!cff_table.ReadU16(&left)) {
+                return OTS_FAILURE();
+              }
+              total += (left + 1);
+            }
+          }
+          break;
+        }
+
+        default:
+          return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_cff_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  file->cff = new OpenTypeCFF;
+  file->cff->data = data;
+  file->cff->length = length;
+  file->cff->font_dict_length = 0;
+  file->cff->local_subrs = NULL;
+
+  // parse "6. Header" in the Adobe Compact Font Format Specification
+  uint8_t major = 0;
+  uint8_t minor = 0;
+  uint8_t hdr_size = 0;
+  uint8_t off_size = 0;
+  if (!table.ReadU8(&major)) {
+    return OTS_FAILURE();
+  }
+  if (!table.ReadU8(&minor)) {
+    return OTS_FAILURE();
+  }
+  if (!table.ReadU8(&hdr_size)) {
+    return OTS_FAILURE();
+  }
+  if (!table.ReadU8(&off_size)) {
+    return OTS_FAILURE();
+  }
+  if ((off_size == 0) || (off_size > 4)) {
+    return OTS_FAILURE();
+  }
+
+  if ((major != 1) ||
+      (minor != 0) ||
+      (hdr_size != 4)) {
+    return OTS_FAILURE();
+  }
+  if (hdr_size >= length) {
+    return OTS_FAILURE();
+  }
+
+  // parse "7. Name INDEX"
+  table.set_offset(hdr_size);
+  CFFIndex name_index;
+  if (!ParseIndex(&table, &name_index)) {
+    return OTS_FAILURE();
+  }
+  if (!ParseNameData(&table, name_index, &(file->cff->name))) {
+    return OTS_FAILURE();
+  }
+
+  // parse "8. Top DICT INDEX"
+  table.set_offset(name_index.offset_to_next);
+  CFFIndex top_dict_index;
+  if (!ParseIndex(&table, &top_dict_index)) {
+    return OTS_FAILURE();
+  }
+  if (name_index.count != top_dict_index.count) {
+    return OTS_FAILURE();
+  }
+
+  // parse "10. String INDEX"
+  table.set_offset(top_dict_index.offset_to_next);
+  CFFIndex string_index;
+  if (!ParseIndex(&table, &string_index)) {
+    return OTS_FAILURE();
+  }
+  if (string_index.count >= 65000 - kNStdString) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const size_t sid_max = string_index.count + kNStdString;
+  // string_index.count == 0 is allowed.
+
+  // parse "9. Top DICT Data"
+  if (!ParseDictData(data, length, top_dict_index,
+                     num_glyphs, sid_max,
+                     DICT_DATA_TOPLEVEL, file->cff)) {
+    return OTS_FAILURE();
+  }
+
+  // parse "16. Global Subrs INDEX"
+  table.set_offset(string_index.offset_to_next);
+  CFFIndex global_subrs_index;
+  if (!ParseIndex(&table, &global_subrs_index)) {
+    return OTS_FAILURE();
+  }
+
+  // Check if all fd_index in FDSelect are valid.
+  std::map<uint16_t, uint8_t>::const_iterator iter;
+  std::map<uint16_t, uint8_t>::const_iterator end = file->cff->fd_select.end();
+  for (iter = file->cff->fd_select.begin(); iter != end; ++iter) {
+    if (iter->second >= file->cff->font_dict_length) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check if all charstrings (font hinting code for each glyph) are valid.
+  for (size_t i = 0; i < file->cff->char_strings_array.size(); ++i) {
+    if (!ValidateType2CharStringIndex(file,
+                                      *(file->cff->char_strings_array.at(i)),
+                                      global_subrs_index,
+                                      file->cff->fd_select,
+                                      file->cff->local_subrs_per_font,
+                                      file->cff->local_subrs,
+                                      &table)) {
+      return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
+    }
+  }
+
+  return true;
+}
+
+bool ots_cff_should_serialise(OpenTypeFile *file) {
+  return file->cff != NULL;
+}
+
+bool ots_cff_serialise(OTSStream *out, OpenTypeFile *file) {
+  // TODO(yusukes): would be better to transcode the data,
+  //                rather than simple memcpy.
+  if (!out->Write(file->cff->data, file->cff->length)) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+void ots_cff_free(OpenTypeFile *file) {
+  if (file->cff) {
+    for (size_t i = 0; i < file->cff->char_strings_array.size(); ++i) {
+      delete (file->cff->char_strings_array)[i];
+    }
+    for (size_t i = 0; i < file->cff->local_subrs_per_font.size(); ++i) {
+      delete (file->cff->local_subrs_per_font)[i];
+    }
+    delete file->cff->local_subrs;
+    delete file->cff;
+  }
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cff.h b/third_party/ots/src/cff.h
new file mode 100644
index 0000000..5584acc
--- /dev/null
+++ b/third_party/ots/src/cff.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CFF_H_
+#define OTS_CFF_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct CFFIndex {
+  CFFIndex()
+      : count(0), off_size(0), offset_to_next(0) {}
+  uint16_t count;
+  uint8_t off_size;
+  std::vector<uint32_t> offsets;
+  uint32_t offset_to_next;
+};
+
+struct OpenTypeCFF {
+  const uint8_t *data;
+  size_t length;
+  // Name INDEX. This name is used in name.cc as a postscript font name.
+  std::string name;
+
+  // The number of fonts the file has.
+  size_t font_dict_length;
+  // A map from glyph # to font #.
+  std::map<uint16_t, uint8_t> fd_select;
+
+  // A list of char strings.
+  std::vector<CFFIndex *> char_strings_array;
+  // A list of Local Subrs associated with FDArrays. Can be empty.
+  std::vector<CFFIndex *> local_subrs_per_font;
+  // A Local Subrs associated with Top DICT. Can be NULL.
+  CFFIndex *local_subrs;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_H_
diff --git a/third_party/ots/src/cff_type2_charstring.cc b/third_party/ots/src/cff_type2_charstring.cc
new file mode 100644
index 0000000..6dd4766
--- /dev/null
+++ b/third_party/ots/src/cff_type2_charstring.cc
@@ -0,0 +1,914 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A parser for the Type 2 Charstring Format.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+
+#include "cff_type2_charstring.h"
+
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <stack>
+#include <string>
+#include <utility>
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+// Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
+// Note #5177.
+const int32_t kMaxSubrsCount = 65536;
+const size_t kMaxCharStringLength = 65535;
+const size_t kMaxArgumentStack = 48;
+const size_t kMaxNumberOfStemHints = 96;
+const size_t kMaxSubrNesting = 10;
+
+// |dummy_result| should be a huge positive integer so callsubr and callgsubr
+// will fail with the dummy value.
+const int32_t dummy_result = INT_MAX;
+
+bool ExecuteType2CharString(ots::OpenTypeFile *file,
+                            size_t call_depth,
+                            const ots::CFFIndex& global_subrs_index,
+                            const ots::CFFIndex& local_subrs_index,
+                            ots::Buffer *cff_table,
+                            ots::Buffer *char_string,
+                            std::stack<int32_t> *argument_stack,
+                            bool *out_found_endchar,
+                            bool *out_found_width,
+                            size_t *in_out_num_stems);
+
+#ifdef DUMP_T2CHARSTRING
+// Converts |op| to a string and returns it.
+const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
+  switch (op) {
+  case ots::kHStem:
+    return "HStem";
+  case ots::kVStem:
+    return "VStem";
+  case ots::kVMoveTo:
+    return "VMoveTo";
+  case ots::kRLineTo:
+    return "RLineTo";
+  case ots::kHLineTo:
+    return "HLineTo";
+  case ots::kVLineTo:
+    return "VLineTo";
+  case ots::kRRCurveTo:
+    return "RRCurveTo";
+  case ots::kCallSubr:
+    return "CallSubr";
+  case ots::kReturn:
+    return "Return";
+  case ots::kEndChar:
+    return "EndChar";
+  case ots::kHStemHm:
+    return "HStemHm";
+  case ots::kHintMask:
+    return "HintMask";
+  case ots::kCntrMask:
+    return "CntrMask";
+  case ots::kRMoveTo:
+    return "RMoveTo";
+  case ots::kHMoveTo:
+    return "HMoveTo";
+  case ots::kVStemHm:
+    return "VStemHm";
+  case ots::kRCurveLine:
+    return "RCurveLine";
+  case ots::kRLineCurve:
+    return "RLineCurve";
+  case ots::kVVCurveTo:
+    return "VVCurveTo";
+  case ots::kHHCurveTo:
+    return "HHCurveTo";
+  case ots::kCallGSubr:
+    return "CallGSubr";
+  case ots::kVHCurveTo:
+    return "VHCurveTo";
+  case ots::kHVCurveTo:
+    return "HVCurveTo";
+  case ots::kDotSection:
+    return "DotSection";
+  case ots::kAnd:
+    return "And";
+  case ots::kOr:
+    return "Or";
+  case ots::kNot:
+    return "Not";
+  case ots::kAbs:
+    return "Abs";
+  case ots::kAdd:
+    return "Add";
+  case ots::kSub:
+    return "Sub";
+  case ots::kDiv:
+    return "Div";
+  case ots::kNeg:
+    return "Neg";
+  case ots::kEq:
+    return "Eq";
+  case ots::kDrop:
+    return "Drop";
+  case ots::kPut:
+    return "Put";
+  case ots::kGet:
+    return "Get";
+  case ots::kIfElse:
+    return "IfElse";
+  case ots::kRandom:
+    return "Random";
+  case ots::kMul:
+    return "Mul";
+  case ots::kSqrt:
+    return "Sqrt";
+  case ots::kDup:
+    return "Dup";
+  case ots::kExch:
+    return "Exch";
+  case ots::kIndex:
+    return "Index";
+  case ots::kRoll:
+    return "Roll";
+  case ots::kHFlex:
+    return "HFlex";
+  case ots::kFlex:
+    return "Flex";
+  case ots::kHFlex1:
+    return "HFlex1";
+  case ots::kFlex1:
+    return "Flex1";
+  }
+
+  return "UNKNOWN";
+}
+#endif
+
+// Read one or more bytes from the |char_string| buffer and stores the number
+// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
+// true on |out_is_operator|. Returns true if the function read a number.
+bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
+                                       int32_t *out_number,
+                                       bool *out_is_operator) {
+  uint8_t v = 0;
+  if (!char_string->ReadU8(&v)) {
+    return OTS_FAILURE();
+  }
+  *out_is_operator = false;
+
+  // The conversion algorithm is described in Adobe Technical Note #5177, page
+  // 13, Table 1.
+  if (v <= 11) {
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v == 12) {
+    uint16_t result = (v << 8);
+    if (!char_string->ReadU8(&v)) {
+      return OTS_FAILURE();
+    }
+    result += v;
+    *out_number = result;
+    *out_is_operator = true;
+  } else if (v <= 27) {
+    // Special handling for v==19 and v==20 are implemented in
+    // ExecuteType2CharStringOperator().
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v == 28) {
+    if (!char_string->ReadU8(&v)) {
+      return OTS_FAILURE();
+    }
+    uint16_t result = (v << 8);
+    if (!char_string->ReadU8(&v)) {
+      return OTS_FAILURE();
+    }
+    result += v;
+    *out_number = result;
+  } else if (v <= 31) {
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v <= 246) {
+    *out_number = static_cast<int32_t>(v) - 139;
+  } else if (v <= 250) {
+    uint8_t w = 0;
+    if (!char_string->ReadU8(&w)) {
+      return OTS_FAILURE();
+    }
+    *out_number = ((static_cast<int32_t>(v) - 247) * 256) +
+        static_cast<int32_t>(w) + 108;
+  } else if (v <= 254) {
+    uint8_t w = 0;
+    if (!char_string->ReadU8(&w)) {
+      return OTS_FAILURE();
+    }
+    *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
+        static_cast<int32_t>(w) - 108;
+  } else if (v == 255) {
+    // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
+    // we should treat the following 4-bytes as a 16.16 fixed-point number
+    // rather than 32bit signed int.
+    if (!char_string->Skip(4)) {
+      return OTS_FAILURE();
+    }
+    *out_number = dummy_result;
+  } else {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+// Executes |op| and updates |argument_stack|. Returns true if the execution
+// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
+// calls ExecuteType2CharString() function. The arguments other than |op| and
+// |argument_stack| are passed for that reason.
+bool ExecuteType2CharStringOperator(ots::OpenTypeFile *file,
+                                    int32_t op,
+                                    size_t call_depth,
+                                    const ots::CFFIndex& global_subrs_index,
+                                    const ots::CFFIndex& local_subrs_index,
+                                    ots::Buffer *cff_table,
+                                    ots::Buffer *char_string,
+                                    std::stack<int32_t> *argument_stack,
+                                    bool *out_found_endchar,
+                                    bool *in_out_found_width,
+                                    size_t *in_out_num_stems) {
+  const size_t stack_size = argument_stack->size();
+
+  switch (op) {
+  case ots::kCallSubr:
+  case ots::kCallGSubr: {
+    const ots::CFFIndex& subrs_index =
+        (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
+
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    int32_t subr_number = argument_stack->top();
+    argument_stack->pop();
+    if (subr_number == dummy_result) {
+      // For safety, we allow subr calls only with immediate subr numbers for
+      // now. For example, we allow "123 callgsubr", but does not allow "100 12
+      // add callgsubr". Please note that arithmetic and conditional operators
+      // always push the |dummy_result| in this implementation.
+      return OTS_FAILURE();
+    }
+
+    // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
+    int32_t bias = 32768;
+    if (subrs_index.count < 1240) {
+      bias = 107;
+    } else if (subrs_index.count < 33900) {
+      bias = 1131;
+    }
+    subr_number += bias;
+
+    // Sanity checks of |subr_number|.
+    if (subr_number < 0) {
+      return OTS_FAILURE();
+    }
+    if (subr_number >= kMaxSubrsCount) {
+      return OTS_FAILURE();
+    }
+    if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
+      return OTS_FAILURE();  // The number is out-of-bounds.
+    }
+
+    // Prepare ots::Buffer where we're going to jump.
+    const size_t length =
+      subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
+    if (length > kMaxCharStringLength) {
+      return OTS_FAILURE();
+    }
+    const size_t offset = subrs_index.offsets[subr_number];
+    cff_table->set_offset(offset);
+    if (!cff_table->Skip(length)) {
+      return OTS_FAILURE();
+    }
+    ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
+
+    return ExecuteType2CharString(file,
+                                  call_depth + 1,
+                                  global_subrs_index,
+                                  local_subrs_index,
+                                  cff_table,
+                                  &char_string_to_jump,
+                                  argument_stack,
+                                  out_found_endchar,
+                                  in_out_found_width,
+                                  in_out_num_stems);
+  }
+
+  case ots::kReturn:
+    return true;
+
+  case ots::kEndChar:
+    *out_found_endchar = true;
+    *in_out_found_width = true;  // just in case.
+    return true;
+
+  case ots::kHStem:
+  case ots::kVStem:
+  case ots::kHStemHm:
+  case ots::kVStemHm: {
+    bool successful = false;
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 2) == 0) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
+      // The -1 is for "width" argument. For details, see Adobe Technical Note
+      // #5177, page 16, note 4.
+      successful = true;
+    }
+    (*in_out_num_stems) += (stack_size / 2);
+    if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;  // always set true since "w" might be 0 byte.
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kRMoveTo: {
+    bool successful = false;
+    if (stack_size == 2) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kVMoveTo:
+  case ots::kHMoveTo: {
+    bool successful = false;
+    if (stack_size == 1) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kHintMask:
+  case ots::kCntrMask: {
+    bool successful = false;
+    if (stack_size == 0) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size == 1)) {
+      // A number for "width" is found.
+      successful = true;
+    } else if ((!(*in_out_found_width)) ||  // in this case, any sizes are ok.
+               ((stack_size % 2) == 0)) {
+      // The numbers are vstem definition.
+      // See Adobe Technical Note #5177, page 24, hintmask.
+      (*in_out_num_stems) += (stack_size / 2);
+      if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+        return OTS_FAILURE();
+      }
+      successful = true;
+    }
+    if (!successful) {
+       return OTS_FAILURE();
+    }
+
+    if ((*in_out_num_stems) == 0) {
+      return OTS_FAILURE();
+    }
+    const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
+    if (!char_string->Skip(mask_bytes)) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return true;
+  }
+
+  case ots::kRLineTo:
+    if (!(*in_out_found_width)) {
+      // The first stack-clearing operator should be one of hstem, hstemhm,
+      // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
+      // endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
+      return OTS_FAILURE();
+    }
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 2) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHLineTo:
+  case ots::kVLineTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRRCurveTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 6) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 6) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRCurveLine:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 8) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 2) % 6) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRLineCurve:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 8) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 6) % 2) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kVVCurveTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size % 4) != 0) &&
+        (((stack_size - 1) % 4) != 0)) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHHCurveTo: {
+    bool successful = false;
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 4) == 0) {
+      // {dxa dxb dyb dxc}+
+      successful = true;
+    } else if (((stack_size - 1) % 4) == 0) {
+      // dy1? {dxa dxb dyb dxc}+
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kVHCurveTo:
+  case ots::kHVCurveTo: {
+    bool successful = false;
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 4) % 8) == 0) {
+      // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
+      successful = true;
+    } else if ((stack_size >= 5) &&
+               ((stack_size - 5) % 8) == 0) {
+      // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
+      successful = true;
+    } else if ((stack_size >= 8) &&
+               ((stack_size - 8) % 8) == 0) {
+      // {dxa dxb dyb dyc dyd dxe dye dxf}+
+      successful = true;
+    } else if ((stack_size >= 9) &&
+               ((stack_size - 9) % 8) == 0) {
+      // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kDotSection:
+    // Deprecated operator but harmless, we probably should drop it some how.
+    if (stack_size != 0) {
+      return OTS_FAILURE();
+    }
+    return true;
+
+  case ots::kAnd:
+  case ots::kOr:
+  case ots::kEq:
+  case ots::kAdd:
+  case ots::kSub:
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kNot:
+  case ots::kAbs:
+  case ots::kNeg:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDiv:
+    // TODO(yusukes): Should detect div-by-zero errors.
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDrop:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    return true;
+
+  case ots::kPut:
+  case ots::kGet:
+  case ots::kIndex:
+    // For now, just call OTS_FAILURE since there is no way to check whether the
+    // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
+    // fonts I have (except malicious ones!) use the operators.
+    // TODO(yusukes): Implement them in a secure way.
+    return OTS_FAILURE();
+
+  case ots::kRoll:
+    // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
+    // whether |N| is smaller than the current stack depth or not.
+    // TODO(yusukes): Implement them in a secure way.
+    return OTS_FAILURE();
+
+  case ots::kRandom:
+    // For now, we don't handle the 'random' operator since the operator makes
+    // it hard to analyze hinting code statically.
+    return OTS_FAILURE();
+
+  case ots::kIfElse:
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kMul:
+    // TODO(yusukes): Should detect overflows.
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kSqrt:
+    // TODO(yusukes): Should check if the argument is negative.
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDup:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    argument_stack->push(dummy_result);
+    if (argument_stack->size() > kMaxArgumentStack) {
+      return OTS_FAILURE();
+    }
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kExch:
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kHFlex:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 7) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kFlex:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 13) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHFlex1:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 9) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kFlex1:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 11) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+  }
+
+  return OTS_FAILURE_MSG("Undefined operator: %d (0x%x)", op, op);
+}
+
+// Executes |char_string| and updates |argument_stack|.
+//
+// call_depth: The current call depth. Initial value is zero.
+// global_subrs_index: Global subroutines.
+// local_subrs_index: Local subroutines for the current glyph.
+// cff_table: A whole CFF table which contains all global and local subroutines.
+// char_string: A charstring we'll execute. |char_string| can be a main routine
+//              in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
+// argument_stack: The stack which an operator in |char_string| operates.
+// out_found_endchar: true is set if |char_string| contains 'endchar'.
+// in_out_found_width: true is set if |char_string| contains 'width' byte (which
+//                     is 0 or 1 byte.)
+// in_out_num_stems: total number of hstems and vstems processed so far.
+bool ExecuteType2CharString(ots::OpenTypeFile *file,
+                            size_t call_depth,
+                            const ots::CFFIndex& global_subrs_index,
+                            const ots::CFFIndex& local_subrs_index,
+                            ots::Buffer *cff_table,
+                            ots::Buffer *char_string,
+                            std::stack<int32_t> *argument_stack,
+                            bool *out_found_endchar,
+                            bool *in_out_found_width,
+                            size_t *in_out_num_stems) {
+  if (call_depth > kMaxSubrNesting) {
+    return OTS_FAILURE();
+  }
+  *out_found_endchar = false;
+
+  const size_t length = char_string->length();
+  while (char_string->offset() < length) {
+    int32_t operator_or_operand = 0;
+    bool is_operator = false;
+    if (!ReadNextNumberFromType2CharString(char_string,
+                                           &operator_or_operand,
+                                           &is_operator)) {
+      return OTS_FAILURE();
+    }
+
+#ifdef DUMP_T2CHARSTRING
+    /*
+      You can dump all operators and operands (except mask bytes for hintmask
+      and cntrmask) by the following code:
+    */
+
+      if (!is_operator) {
+        std::fprintf(stderr, "#%d# ", operator_or_operand);
+      } else {
+        std::fprintf(stderr, "#%s#\n",
+           Type2CharStringOperatorToString(
+               ots::Type2CharStringOperator(operator_or_operand))
+           );
+      }
+#endif
+
+    if (!is_operator) {
+      argument_stack->push(operator_or_operand);
+      if (argument_stack->size() > kMaxArgumentStack) {
+        return OTS_FAILURE();
+      }
+      continue;
+    }
+
+    // An operator is found. Execute it.
+    if (!ExecuteType2CharStringOperator(file,
+                                        operator_or_operand,
+                                        call_depth,
+                                        global_subrs_index,
+                                        local_subrs_index,
+                                        cff_table,
+                                        char_string,
+                                        argument_stack,
+                                        out_found_endchar,
+                                        in_out_found_width,
+                                        in_out_num_stems)) {
+      return OTS_FAILURE();
+    }
+    if (*out_found_endchar) {
+      return true;
+    }
+    if (operator_or_operand == ots::kReturn) {
+      return true;
+    }
+  }
+
+  // No endchar operator is found.
+  return OTS_FAILURE();
+}
+
+// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
+// |out_local_subrs_to_use|. Returns true on success.
+bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
+                     const std::vector<ots::CFFIndex *> &local_subrs_per_font,
+                     const ots::CFFIndex *local_subrs,
+                     uint16_t glyph_index,  // 0-origin
+                     const ots::CFFIndex **out_local_subrs_to_use) {
+  *out_local_subrs_to_use = NULL;
+
+  // First, find local subrs from |local_subrs_per_font|.
+  if ((fd_select.size() > 0) &&
+      (!local_subrs_per_font.empty())) {
+    // Look up FDArray index for the glyph.
+    std::map<uint16_t, uint8_t>::const_iterator iter =
+        fd_select.find(glyph_index);
+    if (iter == fd_select.end()) {
+      return OTS_FAILURE();
+    }
+    const uint8_t fd_index = iter->second;
+    if (fd_index >= local_subrs_per_font.size()) {
+      return OTS_FAILURE();
+    }
+    *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
+  } else if (local_subrs) {
+    // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
+    // entries. If The font has a local subrs index associated with the Top
+    // DICT (not FDArrays), use it.
+    *out_local_subrs_to_use = local_subrs;
+  } else {
+    // Just return NULL.
+    *out_local_subrs_to_use = NULL;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ValidateType2CharStringIndex(
+    ots::OpenTypeFile *file,
+    const CFFIndex& char_strings_index,
+    const CFFIndex& global_subrs_index,
+    const std::map<uint16_t, uint8_t> &fd_select,
+    const std::vector<CFFIndex *> &local_subrs_per_font,
+    const CFFIndex *local_subrs,
+    Buffer* cff_table) {
+  const uint16_t num_offsets =
+      static_cast<uint16_t>(char_strings_index.offsets.size());
+  if (num_offsets != char_strings_index.offsets.size() || num_offsets == 0) {
+    return OTS_FAILURE();  // no charstring.
+  }
+
+  // For each glyph, validate the corresponding charstring.
+  for (uint16_t i = 1; i < num_offsets; ++i) {
+    // Prepare a Buffer object, |char_string|, which contains the charstring
+    // for the |i|-th glyph.
+    const size_t length =
+      char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
+    if (length > kMaxCharStringLength) {
+      return OTS_FAILURE();
+    }
+    const size_t offset = char_strings_index.offsets[i - 1];
+    cff_table->set_offset(offset);
+    if (!cff_table->Skip(length)) {
+      return OTS_FAILURE();
+    }
+    Buffer char_string(cff_table->buffer() + offset, length);
+
+    // Get a local subrs for the glyph.
+    const uint16_t glyph_index = i - 1;  // index in the map is 0-origin.
+    const CFFIndex *local_subrs_to_use = NULL;
+    if (!SelectLocalSubr(fd_select,
+                         local_subrs_per_font,
+                         local_subrs,
+                         glyph_index,
+                         &local_subrs_to_use)) {
+      return OTS_FAILURE();
+    }
+    // If |local_subrs_to_use| is still NULL, use an empty one.
+    CFFIndex default_empty_subrs;
+    if (!local_subrs_to_use){
+      local_subrs_to_use = &default_empty_subrs;
+    }
+
+    // Check a charstring for the |i|-th glyph.
+    std::stack<int32_t> argument_stack;
+    bool found_endchar = false;
+    bool found_width = false;
+    size_t num_stems = 0;
+    if (!ExecuteType2CharString(file,
+                                0 /* initial call_depth is zero */,
+                                global_subrs_index, *local_subrs_to_use,
+                                cff_table, &char_string, &argument_stack,
+                                &found_endchar, &found_width, &num_stems)) {
+      return OTS_FAILURE();
+    }
+    if (!found_endchar) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cff_type2_charstring.h b/third_party/ots/src/cff_type2_charstring.h
new file mode 100644
index 0000000..6732342
--- /dev/null
+++ b/third_party/ots/src/cff_type2_charstring.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CFF_TYPE2_CHARSTRING_H_
+#define OTS_CFF_TYPE2_CHARSTRING_H_
+
+#include "cff.h"
+#include "ots.h"
+
+#include <map>
+#include <vector>
+
+namespace ots {
+
+// Validates all charstrings in |char_strings_index|. Charstring is a small
+// language for font hinting defined in Adobe Technical Note #5177.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+//
+// The validation will fail if one of the following conditions is met:
+//  1. The code uses more than 48 values of argument stack.
+//  2. The code uses deeply nested subroutine calls (more than 10 levels.)
+//  3. The code passes invalid number of operands to an operator.
+//  4. The code calls an undefined global or local subroutine.
+//  5. The code uses one of the following operators that are unlikely used in
+//     an ordinary fonts, and could be dangerous: random, put, get, index, roll.
+//
+// Arguments:
+//  global_subrs_index: Global subroutines which could be called by a charstring
+//                      in |char_strings_index|.
+//  fd_select: A map from glyph # to font #.
+//  local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be
+//                        empty.
+//  local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
+//  cff_table: A buffer which contains actual byte code of charstring, global
+//             subroutines and local subroutines.
+bool ValidateType2CharStringIndex(
+    OpenTypeFile *file,
+    const CFFIndex &char_strings_index,
+    const CFFIndex &global_subrs_index,
+    const std::map<uint16_t, uint8_t> &fd_select,
+    const std::vector<CFFIndex *> &local_subrs_per_font,
+    const CFFIndex *local_subrs,
+    Buffer *cff_table);
+
+// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
+enum Type2CharStringOperator {
+  kHStem = 1,
+  kVStem = 3,
+  kVMoveTo = 4,
+  kRLineTo = 5,
+  kHLineTo = 6,
+  kVLineTo = 7,
+  kRRCurveTo = 8,
+  kCallSubr = 10,
+  kReturn = 11,
+  kEndChar = 14,
+  kHStemHm = 18,
+  kHintMask = 19,
+  kCntrMask = 20,
+  kRMoveTo = 21,
+  kHMoveTo = 22,
+  kVStemHm = 23,
+  kRCurveLine = 24,
+  kRLineCurve = 25,
+  kVVCurveTo = 26,
+  kHHCurveTo = 27,
+  kCallGSubr = 29,
+  kVHCurveTo = 30,
+  kHVCurveTo = 31,
+  kDotSection = 12 << 8,
+  kAnd = (12 << 8) + 3,
+  kOr = (12 << 8) + 4,
+  kNot = (12 << 8) + 5,
+  kAbs = (12 << 8) + 9,
+  kAdd = (12 << 8) + 10,
+  kSub = (12 << 8) + 11,
+  kDiv = (12 << 8) + 12,
+  kNeg = (12 << 8) + 14,
+  kEq = (12 << 8) + 15,
+  kDrop = (12 << 8) + 18,
+  kPut = (12 << 8) + 20,
+  kGet = (12 << 8) + 21,
+  kIfElse = (12 << 8) + 22,
+  kRandom = (12 << 8) + 23,
+  kMul = (12 << 8) + 24,
+  kSqrt = (12 << 8) + 26,
+  kDup = (12 << 8) + 27,
+  kExch = (12 << 8) + 28,
+  kIndex = (12 << 8) + 29,
+  kRoll = (12 << 8) + 30,
+  kHFlex = (12 << 8) + 34,
+  kFlex = (12 << 8) + 35,
+  kHFlex1 = (12 << 8) + 36,
+  kFlex1 = (12 << 8) + 37,
+  // Operators that are undocumented, such as 'blend', will be rejected.
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_TYPE2_CHARSTRING_H_
diff --git a/third_party/ots/src/cmap.cc b/third_party/ots/src/cmap.cc
new file mode 100644
index 0000000..d9ca9fa
--- /dev/null
+++ b/third_party/ots/src/cmap.cc
@@ -0,0 +1,1106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cmap.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "os2.h"
+
+// cmap - Character To Glyph Index Mapping Table
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+#define TABLE_NAME "cmap"
+
+namespace {
+
+struct CMAPSubtableHeader {
+  uint16_t platform;
+  uint16_t encoding;
+  uint32_t offset;
+  uint16_t format;
+  uint32_t length;
+  uint32_t language;
+};
+
+struct Subtable314Range {
+  uint16_t start_range;
+  uint16_t end_range;
+  int16_t id_delta;
+  uint16_t id_range_offset;
+  uint32_t id_range_offset_offset;
+};
+
+// The maximum number of groups in format 12, 13 or 14 subtables.
+// Note: 0xFFFF is the maximum number of glyphs in a single font file.
+const unsigned kMaxCMAPGroups = 0xFFFF;
+
+// Glyph array size for the Mac Roman (format 0) table.
+const size_t kFormat0ArraySize = 256;
+
+// The upper limit of the Unicode code point.
+const uint32_t kUnicodeUpperLimit = 0x10FFFF;
+
+// The maximum number of UVS records (See below).
+const uint32_t kMaxCMAPSelectorRecords = 259;
+// The range of UVSes are:
+//   0x180B-0x180D (3 code points)
+//   0xFE00-0xFE0F (16 code points)
+//   0xE0100-0xE01EF (240 code points)
+const uint32_t kMongolianVSStart = 0x180B;
+const uint32_t kMongolianVSEnd = 0x180D;
+const uint32_t kVSStart = 0xFE00;
+const uint32_t kVSEnd = 0xFE0F;
+const uint32_t kIVSStart = 0xE0100;
+const uint32_t kIVSEnd = 0xE01EF;
+const uint32_t kUVSUpperLimit = 0xFFFFFF;
+
+// Parses Format 4 tables
+bool ParseFormat4(ots::OpenTypeFile *file, int platform, int encoding,
+              const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
+  // whole thing and recompacting it, we validate it and include it verbatim
+  // in the output.
+
+  if (!file->os2) {
+    return OTS_FAILURE_MSG("Required OS/2 table missing");
+  }
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
+  }
+  uint16_t language = 0;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE_MSG("Can't read language");
+  }
+  if (language) {
+    // Platform ID 3 (windows) subtables should have language '0'.
+    return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
+  }
+
+  uint16_t segcountx2, search_range, entry_selector, range_shift;
+  segcountx2 = search_range = entry_selector = range_shift = 0;
+  if (!subtable.ReadU16(&segcountx2) ||
+      !subtable.ReadU16(&search_range) ||
+      !subtable.ReadU16(&entry_selector) ||
+      !subtable.ReadU16(&range_shift)) {
+    return OTS_FAILURE_MSG("Failed to read subcmap structure");
+  }
+
+  if (segcountx2 & 1 || search_range & 1) {
+    return OTS_FAILURE_MSG("Bad subcmap structure");
+  }
+  const uint16_t segcount = segcountx2 >> 1;
+  // There must be at least one segment according the spec.
+  if (segcount < 1) {
+    return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
+  }
+
+  // log2segcount is the maximal x s.t. 2^x < segcount
+  unsigned log2segcount = 0;
+  while (1u << (log2segcount + 1) <= segcount) {
+    log2segcount++;
+  }
+
+  const uint16_t expected_search_range = 2 * 1u << log2segcount;
+  if (expected_search_range != search_range) {
+    return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
+  }
+
+  if (entry_selector != log2segcount) {
+    return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
+  }
+
+  const uint16_t expected_range_shift = segcountx2 - search_range;
+  if (range_shift != expected_range_shift) {
+    return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
+  }
+
+  std::vector<Subtable314Range> ranges(segcount);
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].end_range)) {
+      return OTS_FAILURE_MSG("Failed to read segment %d", i);
+    }
+  }
+
+  uint16_t padding;
+  if (!subtable.ReadU16(&padding)) {
+    return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
+  }
+  if (padding) {
+    return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
+  }
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].start_range)) {
+      return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadS16(&ranges[i].id_delta)) {
+      return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    ranges[i].id_range_offset_offset = subtable.offset();
+    if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
+      return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
+    }
+
+    if (ranges[i].id_range_offset & 1) {
+      // Some font generators seem to put 65535 on id_range_offset
+      // for 0xFFFF-0xFFFF range.
+      // (e.g., many fonts in http://www.princexml.com/fonts/)
+      if (i == segcount - 1u) {
+        OTS_WARNING("bad id_range_offset");
+        ranges[i].id_range_offset = 0;
+        // The id_range_offset value in the transcoded font will not change
+        // since this table is not actually "transcoded" yet.
+      } else {
+        return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
+      }
+    }
+  }
+
+  // ranges must be ascending order, based on the end_code. Ranges may not
+  // overlap.
+  for (unsigned i = 1; i < segcount; ++i) {
+    if ((i == segcount - 1u) &&
+        (ranges[i - 1].start_range == 0xffff) &&
+        (ranges[i - 1].end_range == 0xffff) &&
+        (ranges[i].start_range == 0xffff) &&
+        (ranges[i].end_range == 0xffff)) {
+      // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
+      // We'll accept them as an exception.
+      OTS_WARNING("multiple 0xffff terminators found");
+      continue;
+    }
+
+    // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
+    // unsorted table...
+    if (ranges[i].end_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
+    }
+    if (ranges[i].start_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
+    }
+
+    // On many fonts, the value of {first, last}_char_index are incorrect.
+    // Fix them.
+    if (file->os2->first_char_index != 0xFFFF &&
+        ranges[i].start_range != 0xFFFF &&
+        file->os2->first_char_index > ranges[i].start_range) {
+      file->os2->first_char_index = ranges[i].start_range;
+    }
+    if (file->os2->last_char_index != 0xFFFF &&
+        ranges[i].end_range != 0xFFFF &&
+        file->os2->last_char_index < ranges[i].end_range) {
+      file->os2->last_char_index = ranges[i].end_range;
+    }
+  }
+
+  // The last range must end at 0xffff
+  if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
+    return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
+                           ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
+  }
+
+  // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
+  // each code-point defined in the table and make sure that they are all valid
+  // glyphs and that we don't access anything out-of-bounds.
+  for (unsigned i = 0; i < segcount; ++i) {
+    for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
+      const uint16_t code_point = static_cast<uint16_t>(cp);
+      if (ranges[i].id_range_offset == 0) {
+        // this is explictly allowed to overflow in the spec
+        const uint16_t glyph = code_point + ranges[i].id_delta;
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+        }
+      } else {
+        const uint16_t range_delta = code_point - ranges[i].start_range;
+        // this might seem odd, but it's true. The offset is relative to the
+        // location of the offset value itself.
+        const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
+                                         ranges[i].id_range_offset +
+                                         range_delta * 2;
+        // We need to be able to access a 16-bit value from this offset
+        if (glyph_id_offset + 1 >= length) {
+          return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
+        }
+        uint16_t glyph;
+        std::memcpy(&glyph, data + glyph_id_offset, 2);
+        glyph = ntohs(glyph);
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+        }
+      }
+    }
+  }
+
+  // We accept the table.
+  // TODO(yusukes): transcode the subtable.
+  if (platform == 3 && encoding == 0) {
+    file->cmap->subtable_3_0_4_data = data;
+    file->cmap->subtable_3_0_4_length = length;
+  } else if (platform == 3 && encoding == 1) {
+    file->cmap->subtable_3_1_4_data = data;
+    file->cmap->subtable_3_1_4_length = length;
+  } else if (platform == 0 && encoding == 3) {
+    file->cmap->subtable_0_3_4_data = data;
+    file->cmap->subtable_0_3_4_length = length;
+  } else {
+    return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
+  }
+
+  return true;
+}
+
+bool Parse31012(ots::OpenTypeFile *file,
+                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 12 tables are simple. We parse these and fully serialise them
+  // later.
+
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
+  }
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE_MSG("can't read format 12 subtable language");
+  }
+  if (language) {
+    return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
+  }
+
+  uint32_t num_groups = 0;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
+  }
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE_MSG("bad format 12 subtable group count %d", num_groups);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_3_10_12;
+  groups.resize(num_groups);
+
+  for (unsigned i = 0; i < num_groups; ++i) {
+    if (!subtable.ReadU32(&groups[i].start_range) ||
+        !subtable.ReadU32(&groups[i].end_range) ||
+        !subtable.ReadU32(&groups[i].start_glyph_id)) {
+      return OTS_FAILURE_MSG("can't read format 12 subtable group");
+    }
+
+    if (groups[i].start_range > kUnicodeUpperLimit ||
+        groups[i].end_range > kUnicodeUpperLimit ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
+                             groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+    }
+
+    // [0xD800, 0xDFFF] are surrogate code points.
+    if (groups[i].start_range >= 0xD800 &&
+        groups[i].start_range <= 0xDFFF) {
+      return OTS_FAILURE_MSG("format 12 subtable out of range group startCharCode (0x%4X)", groups[i].start_range);
+    }
+    if (groups[i].end_range >= 0xD800 &&
+        groups[i].end_range <= 0xDFFF) {
+      return OTS_FAILURE_MSG("format 12 subtable out of range group endCharCode (0x%4X)", groups[i].end_range);
+    }
+    if (groups[i].start_range < 0xD800 &&
+        groups[i].end_range > 0xDFFF) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group startCharCode (0x%4X) or endCharCode (0x%4X)",
+                             groups[i].start_range, groups[i].end_range);
+    }
+
+    // We assert that the glyph value is within range. Because of the range
+    // limits, above, we don't need to worry about overflow.
+    if (groups[i].end_range < groups[i].start_range) {
+      return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
+                             groups[i].end_range, groups[i].start_range);
+    }
+    if ((groups[i].end_range - groups[i].start_range) +
+        groups[i].start_glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
+    }
+  }
+
+  // the groups must be sorted by start code and may not overlap
+  for (unsigned i = 1; i < num_groups; ++i) {
+    if (groups[i].start_range <= groups[i - 1].start_range) {
+      return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
+                             groups[i].start_range, groups[i-1].start_range);
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
+                             groups[i].start_range, groups[i-1].end_range);
+    }
+  }
+
+  return true;
+}
+
+bool Parse31013(ots::OpenTypeFile *file,
+                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 13 tables are simple. We parse these and fully serialise them
+  // later.
+
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Bad cmap subtable length");
+  }
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE_MSG("Can't read cmap subtable language");
+  }
+  if (language) {
+    return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
+  }
+
+  uint32_t num_groups = 0;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
+  }
+
+  // We limit the number of groups in the same way as in 3.10.12 tables. See
+  // the comment there in
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE_MSG("Bad number of groups (%d) in a cmap subtable", num_groups);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_3_10_13;
+  groups.resize(num_groups);
+
+  for (unsigned i = 0; i < num_groups; ++i) {
+    if (!subtable.ReadU32(&groups[i].start_range) ||
+        !subtable.ReadU32(&groups[i].end_range) ||
+        !subtable.ReadU32(&groups[i].start_glyph_id)) {
+      return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
+    }
+
+    // We conservatively limit all of the values to protect some parsers from
+    // overflows
+    if (groups[i].start_range > kUnicodeUpperLimit ||
+        groups[i].end_range > kUnicodeUpperLimit ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+    }
+
+    if (groups[i].start_glyph_id >= num_glyphs) {
+      return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
+    }
+  }
+
+  // the groups must be sorted by start code and may not overlap
+  for (unsigned i = 1; i < num_groups; ++i) {
+    if (groups[i].start_range <= groups[i - 1].start_range) {
+      return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
+    }
+  }
+
+  return true;
+}
+
+bool Parse0514(ots::OpenTypeFile *file,
+               const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  // Unicode Variation Selector table
+  ots::Buffer subtable(data, length);
+
+  // Format 14 tables are simple. We parse these and fully serialise them
+  // later.
+
+  // Skip format (USHORT) and length (ULONG)
+  if (!subtable.Skip(6)) {
+    return OTS_FAILURE_MSG("Can't read start of cmap subtable");
+  }
+
+  uint32_t num_records = 0;
+  if (!subtable.ReadU32(&num_records)) {
+    return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
+  }
+  if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
+    return OTS_FAILURE_MSG("Bad number of records (%d) in cmap subtable", num_records);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
+      = file->cmap->subtable_0_5_14;
+  records.resize(num_records);
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    if (!subtable.ReadU24(&records[i].var_selector) ||
+        !subtable.ReadU32(&records[i].default_offset) ||
+        !subtable.ReadU32(&records[i].non_default_offset)) {
+      return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
+    }
+    // Checks the value of variation selector
+    if (!((records[i].var_selector >= kMongolianVSStart &&
+           records[i].var_selector <= kMongolianVSEnd) ||
+          (records[i].var_selector >= kVSStart &&
+           records[i].var_selector <= kVSEnd) ||
+          (records[i].var_selector >= kIVSStart &&
+           records[i].var_selector <= kIVSEnd))) {
+      return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
+    }
+    if (i > 0 &&
+        records[i-1].var_selector >= records[i].var_selector) {
+      return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
+    }
+
+    // Checks offsets
+    if (!records[i].default_offset && !records[i].non_default_offset) {
+      return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
+    }
+    if (records[i].default_offset &&
+        records[i].default_offset >= length) {
+      return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
+    }
+    if (records[i].non_default_offset &&
+        records[i].non_default_offset >= length) {
+      return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
+    }
+  }
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    // Checks default UVS table
+    if (records[i].default_offset) {
+      subtable.set_offset(records[i].default_offset);
+      uint32_t num_ranges = 0;
+      if (!subtable.ReadU32(&num_ranges)) {
+        return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
+      }
+      if (!num_ranges || num_ranges > kMaxCMAPGroups) {
+        return OTS_FAILURE_MSG("number of ranges too high (%d > %d) in record %d", num_ranges, kMaxCMAPGroups, i);
+      }
+
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
+          = records[i].ranges;
+      ranges.resize(num_ranges);
+
+      for (unsigned j = 0; j < num_ranges; ++j) {
+        if (!subtable.ReadU24(&ranges[j].unicode_value) ||
+            !subtable.ReadU8(&ranges[j].additional_count)) {
+          return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
+        }
+        const uint32_t check_value =
+            ranges[j].unicode_value + ranges[j].additional_count;
+        if (ranges[j].unicode_value == 0 ||
+            ranges[j].unicode_value > kUnicodeUpperLimit ||
+            check_value > kUVSUpperLimit ||
+            (last_unicode_value &&
+             ranges[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
+        }
+        last_unicode_value = check_value;
+      }
+    }
+
+    // Checks non default UVS table
+    if (records[i].non_default_offset) {
+      subtable.set_offset(records[i].non_default_offset);
+      uint32_t num_mappings = 0;
+      if (!subtable.ReadU32(&num_mappings)) {
+        return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
+      }
+      if (!num_mappings || num_mappings > kMaxCMAPGroups) {
+        return OTS_FAILURE_MSG("Number of mappings too high (%d) in variation selector record %d", num_mappings, i);
+      }
+
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
+          = records[i].mappings;
+      mappings.resize(num_mappings);
+
+      for (unsigned j = 0; j < num_mappings; ++j) {
+        if (!subtable.ReadU24(&mappings[j].unicode_value) ||
+            !subtable.ReadU16(&mappings[j].glyph_id)) {
+          return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
+        }
+        if (mappings[j].glyph_id == 0 ||
+            mappings[j].unicode_value == 0 ||
+            mappings[j].unicode_value > kUnicodeUpperLimit ||
+            (last_unicode_value &&
+             mappings[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
+        }
+        last_unicode_value = mappings[j].unicode_value;
+      }
+    }
+  }
+
+  if (subtable.offset() != length) {
+    return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
+  }
+  file->cmap->subtable_0_5_14_length = subtable.offset();
+  return true;
+}
+
+bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Mac Roman table
+  ots::Buffer subtable(data, length);
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Bad cmap subtable");
+  }
+  uint16_t language = 0;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE_MSG("Can't read language in cmap subtable");
+  }
+  if (language) {
+    // simsun.ttf has non-zero language id.
+    OTS_WARNING("language id should be zero: %u", language);
+  }
+
+  file->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
+  for (size_t i = 0; i < kFormat0ArraySize; ++i) {
+    uint8_t glyph_id = 0;
+    if (!subtable.ReadU8(&glyph_id)) {
+      return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
+    }
+    file->cmap->subtable_1_0_0.push_back(glyph_id);
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_cmap_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->cmap = new OpenTypeCMAP;
+
+  uint16_t version = 0;
+  uint16_t num_tables = 0;
+  if (!table.ReadU16(&version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE_MSG("Can't read structure of cmap");
+  }
+
+  if (version != 0) {
+    return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
+  }
+  if (!num_tables) {
+    return OTS_FAILURE_MSG("No subtables in cmap!");
+  }
+
+  std::vector<CMAPSubtableHeader> subtable_headers;
+
+  // read the subtable headers
+  subtable_headers.reserve(num_tables);
+  for (unsigned i = 0; i < num_tables; ++i) {
+    CMAPSubtableHeader subt;
+
+    if (!table.ReadU16(&subt.platform) ||
+        !table.ReadU16(&subt.encoding) ||
+        !table.ReadU32(&subt.offset)) {
+      return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
+    }
+
+    subtable_headers.push_back(subt);
+  }
+
+  const size_t data_offset = table.offset();
+
+  // make sure that all the offsets are valid.
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
+    }
+    if (subtable_headers[i].offset < data_offset ||
+        subtable_headers[i].offset >= length) {
+      return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
+    }
+  }
+
+  // the format of the table is the first couple of bytes in the table. The
+  // length of the table is stored in a format-specific way.
+  for (unsigned i = 0; i < num_tables; ++i) {
+    table.set_offset(subtable_headers[i].offset);
+    if (!table.ReadU16(&subtable_headers[i].format)) {
+      return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
+    }
+
+    uint16_t len = 0;
+    uint16_t lang = 0;
+    switch (subtable_headers[i].format) {
+      case 0:
+      case 4:
+        if (!table.ReadU16(&len)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+        }
+        if (!table.ReadU16(&lang)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+        }
+        subtable_headers[i].length = len;
+        subtable_headers[i].language = lang;
+        break;
+      case 12:
+      case 13:
+        if (!table.Skip(2)) {
+          return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
+        }
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
+        }
+        if (!table.ReadU32(&subtable_headers[i].language)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+        }
+        break;
+      case 14:
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+        }
+        subtable_headers[i].language = 0;
+        break;
+      default:
+        subtable_headers[i].length = 0;
+        subtable_headers[i].language = 0;
+        break;
+    }
+  }
+
+  // check if the table is sorted first by platform ID, then by encoding ID.
+  uint32_t last_id = 0;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    uint32_t current_id
+        = (subtable_headers[i].platform << 24)
+        + (subtable_headers[i].encoding << 16)
+        + subtable_headers[i].language;
+    if ((i != 0) && (last_id >= current_id)) {
+      return OTS_FAILURE_MSG("subtable %d with platform ID %d, encoding ID %d, language ID %d "
+                             "following subtable with platform ID %d, encoding ID %d, language ID %d",
+                             i,
+                             (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id),
+                             (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id));
+    }
+    last_id = current_id;
+  }
+
+  // Now, verify that all the lengths are sane
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (!subtable_headers[i].length) continue;
+    if (subtable_headers[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
+    }
+    // We know that both the offset and length are < 1GB, so the following
+    // addition doesn't overflow
+    const uint32_t end_byte
+        = subtable_headers[i].offset + subtable_headers[i].length;
+    if (end_byte > length) {
+      return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
+    }
+  }
+
+  // check that the cmap subtables are not overlapping.
+  std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
+  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    const uint32_t end_byte
+        = subtable_headers[i].offset + subtable_headers[i].length;
+
+    if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
+                                            end_byte)).second) {
+      // Sometimes Unicode table and MS table share exactly the same data.
+      // We'll allow this.
+      continue;
+    }
+    overlap_checker.push_back(
+        std::make_pair(subtable_headers[i].offset,
+                       static_cast<uint8_t>(1) /* start */));
+    overlap_checker.push_back(
+        std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
+  }
+  std::sort(overlap_checker.begin(), overlap_checker.end());
+  int overlap_count = 0;
+  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+    overlap_count += (overlap_checker[i].second ? 1 : -1);
+    if (overlap_count > 1) {
+      return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
+    }
+  }
+
+  // we grab the number of glyphs in the file from the maxp table to make sure
+  // that the character map isn't referencing anything beyound this range.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  // We only support a subset of the possible character map tables. Microsoft
+  // 'strongly recommends' that everyone supports the Unicode BMP table with
+  // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
+  //   Platform ID   Encoding ID  Format
+  //   0             0            4       (Unicode Default)
+  //   0             1            4       (Unicode 1.1)
+  //   0             3            4       (Unicode BMP)
+  //   0             3            12      (Unicode UCS-4)
+  //   0             5            14      (Unicode Variation Sequences)
+  //   1             0            0       (Mac Roman)
+  //   3             0            4       (MS Symbol)
+  //   3             1            4       (MS Unicode BMP)
+  //   3             10           12      (MS Unicode UCS-4)
+  //   3             10           13      (MS UCS-4 Fallback mapping)
+  //
+  // Note:
+  //  * 0-0-4 and 0-1-4 tables are (usually) written as a 3-1-4 table. If 3-1-4 table
+  //    also exists, the 0-0-4 or 0-1-4 tables are ignored.
+  //  * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
+  //    Some fonts which include 0-5-14 table seems to be required 0-3-4
+  //    table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
+  //  * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
+  //    exists, the 0-3-12 table is ignored.
+  //
+
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].platform == 0) {
+      // Unicode platform
+
+      if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) &&
+          (subtable_headers[i].format == 4)) {
+        // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4
+        // table actually points to MS symbol data and thus should be parsed as
+        // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
+        // recovered in ots_cmap_serialise().
+        if (!ParseFormat4(file, 3, 1, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 4)) {
+        // parse and output the 0-3-4 table as 0-3-4 table.
+        if (!ParseFormat4(file, 0, 3, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 12)) {
+        // parse and output the 0-3-12 table as 3-10-12 table.
+        if (!Parse31012(file, data + subtable_headers[i].offset,
+                        subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 5) &&
+                 (subtable_headers[i].format == 14)) {
+        if (!Parse0514(file, data + subtable_headers[i].offset,
+                       subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
+        }
+      }
+    } else if (subtable_headers[i].platform == 1) {
+      // Mac platform
+
+      if ((subtable_headers[i].encoding == 0) &&
+          (subtable_headers[i].format == 0)) {
+        // parse and output the 1-0-0 table.
+        if (!Parse100(file, data + subtable_headers[i].offset,
+                      subtable_headers[i].length)) {
+          return OTS_FAILURE();
+        }
+      }
+    } else if (subtable_headers[i].platform == 3) {
+      // MS platform
+
+      switch (subtable_headers[i].encoding) {
+        case 0:
+        case 1:
+          if (subtable_headers[i].format == 4) {
+            // parse 3-0-4 or 3-1-4 table.
+            if (!ParseFormat4(file, subtable_headers[i].platform,
+                          subtable_headers[i].encoding,
+                          data + subtable_headers[i].offset,
+                          subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          }
+          break;
+        case 10:
+          if (subtable_headers[i].format == 12) {
+            file->cmap->subtable_3_10_12.clear();
+            if (!Parse31012(file, data + subtable_headers[i].offset,
+                            subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          } else if (subtable_headers[i].format == 13) {
+            file->cmap->subtable_3_10_13.clear();
+            if (!Parse31013(file, data + subtable_headers[i].offset,
+                            subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          }
+          break;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ots_cmap_should_serialise(OpenTypeFile *file) {
+  return file->cmap != NULL;
+}
+
+bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
+  const bool have_034 = file->cmap->subtable_0_3_4_data != NULL;
+  const bool have_0514 = file->cmap->subtable_0_5_14.size() != 0;
+  const bool have_100 = file->cmap->subtable_1_0_0.size() != 0;
+  const bool have_304 = file->cmap->subtable_3_0_4_data != NULL;
+  // MS Symbol and MS Unicode tables should not co-exist.
+  // See the comment above in 0-0-4 parser.
+  const bool have_314 = (!have_304) && file->cmap->subtable_3_1_4_data;
+  const bool have_31012 = file->cmap->subtable_3_10_12.size() != 0;
+  const bool have_31013 = file->cmap->subtable_3_10_13.size() != 0;
+  const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
+                                 static_cast<uint16_t>(have_0514) +
+                                 static_cast<uint16_t>(have_100) +
+                                 static_cast<uint16_t>(have_304) +
+                                 static_cast<uint16_t>(have_314) +
+                                 static_cast<uint16_t>(have_31012) +
+                                 static_cast<uint16_t>(have_31013);
+  const off_t table_start = out->Tell();
+
+  // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
+  // (e.g., old fonts for Mac). We don't support them.
+  if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
+    return OTS_FAILURE_MSG("no supported subtables were found");
+  }
+
+  if (!out->WriteU16(0) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t record_offset = out->Tell();
+  if (!out->Pad(num_subtables * 8)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t offset_034 = out->Tell();
+  if (have_034) {
+    if (!out->Write(file->cmap->subtable_0_3_4_data,
+                    file->cmap->subtable_0_3_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_0514 = out->Tell();
+  if (have_0514) {
+    const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
+        = file->cmap->subtable_0_5_14;
+    const unsigned num_records = records.size();
+    if (!out->WriteU16(14) ||
+        !out->WriteU32(file->cmap->subtable_0_5_14_length) ||
+        !out->WriteU32(num_records)) {
+      return OTS_FAILURE();
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (!out->WriteU24(records[i].var_selector) ||
+          !out->WriteU32(records[i].default_offset) ||
+          !out->WriteU32(records[i].non_default_offset)) {
+        return OTS_FAILURE();
+      }
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (records[i].default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
+            = records[i].ranges;
+        const unsigned num_ranges = ranges.size();
+        if (!out->Seek(records[i].default_offset + offset_0514) ||
+            !out->WriteU32(num_ranges)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_ranges; ++j) {
+          if (!out->WriteU24(ranges[j].unicode_value) ||
+              !out->WriteU8(ranges[j].additional_count)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+      if (records[i].non_default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
+            = records[i].mappings;
+        const unsigned num_mappings = mappings.size();
+        if (!out->Seek(records[i].non_default_offset + offset_0514) ||
+            !out->WriteU32(num_mappings)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_mappings; ++j) {
+          if (!out->WriteU24(mappings[j].unicode_value) ||
+              !out->WriteU16(mappings[j].glyph_id)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+    }
+  }
+
+  const off_t offset_100 = out->Tell();
+  if (have_100) {
+    if (!out->WriteU16(0) ||  // format
+        !out->WriteU16(6 + kFormat0ArraySize) ||  // length
+        !out->WriteU16(0)) {  // language
+      return OTS_FAILURE();
+    }
+    if (!out->Write(&(file->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_304 = out->Tell();
+  if (have_304) {
+    if (!out->Write(file->cmap->subtable_3_0_4_data,
+                    file->cmap->subtable_3_0_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_314 = out->Tell();
+  if (have_314) {
+    if (!out->Write(file->cmap->subtable_3_1_4_data,
+                    file->cmap->subtable_3_1_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_31012 = out->Tell();
+  if (have_31012) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups
+        = file->cmap->subtable_3_10_12;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(12) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 16) ||
+        !out->WriteU32(0) ||
+        !out->WriteU32(num_groups)) {
+      return OTS_FAILURE();
+    }
+
+    for (unsigned i = 0; i < num_groups; ++i) {
+      if (!out->WriteU32(groups[i].start_range) ||
+          !out->WriteU32(groups[i].end_range) ||
+          !out->WriteU32(groups[i].start_glyph_id)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  const off_t offset_31013 = out->Tell();
+  if (have_31013) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups
+        = file->cmap->subtable_3_10_13;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(13) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 16) ||
+        !out->WriteU32(0) ||
+        !out->WriteU32(num_groups)) {
+      return OTS_FAILURE();
+    }
+
+    for (unsigned i = 0; i < num_groups; ++i) {
+      if (!out->WriteU32(groups[i].start_range) ||
+          !out->WriteU32(groups[i].end_range) ||
+          !out->WriteU32(groups[i].start_glyph_id)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  const off_t table_end = out->Tell();
+  // We might have hanging bytes from the above's checksum which the OTSStream
+  // then merges into the table of offsets.
+  OTSStream::ChecksumState saved_checksum = out->SaveChecksumState();
+  out->ResetChecksum();
+
+  // Now seek back and write the table of offsets
+  if (!out->Seek(record_offset)) {
+    return OTS_FAILURE();
+  }
+
+  if (have_034) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(3) ||
+        !out->WriteU32(offset_034 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_0514) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(5) ||
+        !out->WriteU32(offset_0514 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_100) {
+    if (!out->WriteU16(1) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(offset_100 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_304) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(offset_304 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_314) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(1) ||
+        !out->WriteU32(offset_314 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_31012) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(10) ||
+        !out->WriteU32(offset_31012 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_31013) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(10) ||
+        !out->WriteU32(offset_31013 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (!out->Seek(table_end)) {
+    return OTS_FAILURE();
+  }
+  out->RestoreChecksum(saved_checksum);
+
+  return true;
+}
+
+void ots_cmap_free(OpenTypeFile *file) {
+  delete file->cmap;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cmap.h b/third_party/ots/src/cmap.h
new file mode 100644
index 0000000..5b09556
--- /dev/null
+++ b/third_party/ots/src/cmap.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CMAP_H_
+#define OTS_CMAP_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCMAPSubtableRange {
+  uint32_t start_range;
+  uint32_t end_range;
+  uint32_t start_glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRange {
+  uint32_t unicode_value;
+  uint8_t additional_count;
+};
+
+struct OpenTypeCMAPSubtableVSMapping {
+  uint32_t unicode_value;
+  uint16_t glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRecord {
+  uint32_t var_selector;
+  uint32_t default_offset;
+  uint32_t non_default_offset;
+  std::vector<OpenTypeCMAPSubtableVSRange> ranges;
+  std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
+};
+
+struct OpenTypeCMAP {
+  OpenTypeCMAP()
+      : subtable_0_3_4_data(NULL),
+        subtable_0_3_4_length(0),
+        subtable_0_5_14_length(0),
+        subtable_3_0_4_data(NULL),
+        subtable_3_0_4_length(0),
+        subtable_3_1_4_data(NULL),
+        subtable_3_1_4_length(0) {
+  }
+
+  // Platform 0, Encoding 3, Format 4, Unicode BMP table.
+  const uint8_t *subtable_0_3_4_data;
+  size_t subtable_0_3_4_length;
+
+  // Platform 0, Encoding 5, Format 14, Unicode Variation Sequence table.
+  size_t subtable_0_5_14_length;
+  std::vector<OpenTypeCMAPSubtableVSRecord> subtable_0_5_14;
+
+  // Platform 3, Encoding 0, Format 4, MS Symbol table.
+  const uint8_t *subtable_3_0_4_data;
+  size_t subtable_3_0_4_length;
+  // Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
+  const uint8_t *subtable_3_1_4_data;
+  size_t subtable_3_1_4_length;
+
+  // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
+  std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
+  // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
+  std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
+  // Platform 1, Encoding 0, Format 0, Mac Roman table.
+  std::vector<uint8_t> subtable_1_0_0;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/third_party/ots/src/cvt.cc b/third_party/ots/src/cvt.cc
new file mode 100644
index 0000000..f83ad0e
--- /dev/null
+++ b/third_party/ots/src/cvt.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cvt.h"
+
+// cvt - Control Value Table
+// http://www.microsoft.com/typography/otspec/cvt.htm
+
+#define TABLE_NAME "cvt"
+
+namespace ots {
+
+bool ots_cvt_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeCVT *cvt = new OpenTypeCVT;
+  file->cvt = cvt;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("Length (%d) > 120K");  // almost all cvt tables are less than 4k bytes.
+  }
+
+  if (length % 2 != 0) {
+    return OTS_FAILURE_MSG("Uneven cvt length (%d)", length);
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Length too high");
+  }
+
+  cvt->data = data;
+  cvt->length = length;
+  return true;
+}
+
+bool ots_cvt_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) {
+    return false;  // this table is not for CFF fonts.
+  }
+  return file->cvt != NULL;
+}
+
+bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeCVT *cvt = file->cvt;
+
+  if (!out->Write(cvt->data, cvt->length)) {
+    return OTS_FAILURE_MSG("Failed to write CVT table");
+  }
+
+  return true;
+}
+
+void ots_cvt_free(OpenTypeFile *file) {
+  delete file->cvt;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cvt.h b/third_party/ots/src/cvt.h
new file mode 100644
index 0000000..3c25f06
--- /dev/null
+++ b/third_party/ots/src/cvt.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CVT_H_
+#define OTS_CVT_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCVT {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CVT_H_
diff --git a/third_party/ots/src/fpgm.cc b/third_party/ots/src/fpgm.cc
new file mode 100644
index 0000000..eba03e7
--- /dev/null
+++ b/third_party/ots/src/fpgm.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpgm.h"
+
+// fpgm - Font Program
+// http://www.microsoft.com/typography/otspec/fpgm.htm
+
+#define TABLE_NAME "fpgm"
+
+namespace ots {
+
+bool ots_fpgm_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeFPGM *fpgm = new OpenTypeFPGM;
+  file->fpgm = fpgm;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("length (%ld) > 120", length);  // almost all fpgm tables are less than 5k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Bad fpgm length");
+  }
+
+  fpgm->data = data;
+  fpgm->length = length;
+  return true;
+}
+
+bool ots_fpgm_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->fpgm != NULL;
+}
+
+bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeFPGM *fpgm = file->fpgm;
+
+  if (!out->Write(fpgm->data, fpgm->length)) {
+    return OTS_FAILURE_MSG("Failed to write fpgm");
+  }
+
+  return true;
+}
+
+void ots_fpgm_free(OpenTypeFile *file) {
+  delete file->fpgm;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/fpgm.h b/third_party/ots/src/fpgm.h
new file mode 100644
index 0000000..8fabac3
--- /dev/null
+++ b/third_party/ots/src/fpgm.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_FPGM_H_
+#define OTS_FPGM_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeFPGM {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FPGM_H_
diff --git a/third_party/ots/src/gasp.cc b/third_party/ots/src/gasp.cc
new file mode 100644
index 0000000..1e2327f
--- /dev/null
+++ b/third_party/ots/src/gasp.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gasp.h"
+
+// gasp - Grid-fitting And Scan-conversion Procedure
+// http://www.microsoft.com/typography/otspec/gasp.htm
+
+#define TABLE_NAME "gasp"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->gasp; \
+    file->gasp = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_gasp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeGASP *gasp = new OpenTypeGASP;
+  file->gasp = gasp;
+
+  uint16_t num_ranges = 0;
+  if (!table.ReadU16(&gasp->version) ||
+      !table.ReadU16(&num_ranges)) {
+    return OTS_FAILURE_MSG("Failed to read table header");
+  }
+
+  if (gasp->version > 1) {
+    // Lots of Linux fonts have bad version numbers...
+    DROP_THIS_TABLE("bad version: %u", gasp->version);
+    return true;
+  }
+
+  if (num_ranges == 0) {
+    DROP_THIS_TABLE("num_ranges is zero");
+    return true;
+  }
+
+  gasp->gasp_ranges.reserve(num_ranges);
+  for (unsigned i = 0; i < num_ranges; ++i) {
+    uint16_t max_ppem = 0;
+    uint16_t behavior = 0;
+    if (!table.ReadU16(&max_ppem) ||
+        !table.ReadU16(&behavior)) {
+      return OTS_FAILURE_MSG("Failed to read subrange %d", i);
+    }
+    if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
+      // The records in the gaspRange[] array must be sorted in order of
+      // increasing rangeMaxPPEM value.
+      DROP_THIS_TABLE("ranges are not sorted");
+      return true;
+    }
+    if ((i == num_ranges - 1u) &&  // never underflow.
+        (max_ppem != 0xffffu)) {
+      DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
+                  "for rangeMaxPPEM");
+      return true;
+    }
+
+    if (behavior >> 8) {
+      OTS_WARNING("undefined bits are used: %x", behavior);
+      // mask undefined bits.
+      behavior &= 0x000fu;
+    }
+
+    if (gasp->version == 0 && (behavior >> 2) != 0) {
+      OTS_WARNING("changed the version number to 1");
+      gasp->version = 1;
+    }
+
+    gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
+  }
+
+  return true;
+}
+
+bool ots_gasp_should_serialise(OpenTypeFile *file) {
+  return file->gasp != NULL;
+}
+
+bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGASP *gasp = file->gasp;
+
+  const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
+  if (num_ranges != gasp->gasp_ranges.size() ||
+      !out->WriteU16(gasp->version) ||
+      !out->WriteU16(num_ranges)) {
+    return OTS_FAILURE_MSG("failed to write gasp header");
+  }
+
+  for (uint16_t i = 0; i < num_ranges; ++i) {
+    if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
+        !out->WriteU16(gasp->gasp_ranges[i].second)) {
+      return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_gasp_free(OpenTypeFile *file) {
+  delete file->gasp;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gasp.h b/third_party/ots/src/gasp.h
new file mode 100644
index 0000000..48d7e7c
--- /dev/null
+++ b/third_party/ots/src/gasp.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GASP_H_
+#define OTS_GASP_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGASP {
+  uint16_t version;
+  // A array of (max PPEM, GASP behavior) pairs.
+  std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GASP_H_
diff --git a/third_party/ots/src/gdef.cc b/third_party/ots/src/gdef.cc
new file mode 100644
index 0000000..4553d58
--- /dev/null
+++ b/third_party/ots/src/gdef.cc
@@ -0,0 +1,388 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gdef.h"
+
+#include <limits>
+#include <vector>
+
+#include "gpos.h"
+#include "gsub.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GDEF - The Glyph Definition Table
+// http://www.microsoft.com/typography/otspec/gdef.htm
+
+#define TABLE_NAME "GDEF"
+
+namespace {
+
+// The maximum class value in class definition tables.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+// The maximum class value in the glyph class definision table.
+const uint16_t kMaxGlyphClassDefValue = 4;
+// The maximum format number of caret value tables.
+// We don't support format 3 for now. See the comment in
+// ParseLigCaretListTable() for the reason.
+const uint16_t kMaxCaretValueFormat = 2;
+
+bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(file, data, length, num_glyphs,
+                                 kMaxGlyphClassDefValue);
+}
+
+bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                          size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read gdef header");
+  }
+  const unsigned attach_points_end =
+      2 * static_cast<unsigned>(glyph_count) + 4;
+  if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad glyph count in gdef");
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < attach_points_end) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
+  }
+
+  std::vector<uint16_t> attach_points;
+  attach_points.resize(glyph_count);
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    if (!subtable.ReadU16(&attach_points[i])) {
+      return OTS_FAILURE_MSG("Can't read attachment point %d", i);
+    }
+    if (attach_points[i] >= length ||
+        attach_points[i] < attach_points_end) {
+      return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Bad coverage table");
+  }
+
+  // Parse attach point table
+  for (unsigned i = 0; i < attach_points.size(); ++i) {
+    subtable.set_offset(attach_points[i]);
+    uint16_t point_count = 0;
+    if (!subtable.ReadU16(&point_count)) {
+      return OTS_FAILURE_MSG("Can't read point count %d", i);
+    }
+    if (point_count == 0) {
+      return OTS_FAILURE_MSG("zero point count %d", i);
+    }
+    uint16_t last_point_index = 0;
+    uint16_t point_index = 0;
+    for (unsigned j = 0; j < point_count; ++j) {
+      if (!subtable.ReadU16(&point_index)) {
+        return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
+      }
+      // Contour point indeces are in increasing numerical order
+      if (last_point_index != 0 && last_point_index >= point_index) {
+        return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
+                    last_point_index, point_index);
+      }
+      last_point_index = point_index;
+    }
+  }
+  return true;
+}
+
+bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t offset_coverage = 0;
+  uint16_t lig_glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_glyph_count)) {
+    return OTS_FAILURE_MSG("Can't read caret structure");
+  }
+  const unsigned lig_glyphs_end =
+      2 * static_cast<unsigned>(lig_glyph_count) + 4;
+  if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad caret structure");
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < lig_glyphs_end) {
+    return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
+  }
+  if (lig_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
+  }
+
+  std::vector<uint16_t> lig_glyphs;
+  lig_glyphs.resize(lig_glyph_count);
+  for (unsigned i = 0; i < lig_glyph_count; ++i) {
+    if (!subtable.ReadU16(&lig_glyphs[i])) {
+      return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
+    }
+    if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
+      return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Can't parse caret coverage table");
+  }
+
+  // Parse ligature glyph table
+  for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
+    subtable.set_offset(lig_glyphs[i]);
+    uint16_t caret_count = 0;
+    if (!subtable.ReadU16(&caret_count)) {
+      return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
+    }
+    if (caret_count == 0) {
+      return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
+    }
+
+    std::vector<uint16_t> caret_value_offsets;
+    caret_value_offsets.resize(caret_count);
+    unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
+    for (unsigned j = 0; j < caret_count; ++j) {
+      if (!subtable.ReadU16(&caret_value_offsets[j])) {
+        return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
+      }
+      if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
+        return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
+      }
+    }
+
+    // Parse caret values table
+    for (unsigned j = 0; j < caret_count; ++j) {
+      subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
+      uint16_t caret_format = 0;
+      if (!subtable.ReadU16(&caret_format)) {
+        return OTS_FAILURE_MSG("Can't read caret values table %d in glyph %d", j, i);
+      }
+      // TODO(bashi): We only support caret value format 1 and 2 for now
+      // because there are no fonts which contain caret value format 3
+      // as far as we investigated.
+      if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
+        return OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
+      }
+      // CaretValueFormats contain a 2-byte field which could be
+      // arbitrary value.
+      if (!subtable.Skip(2)) {
+        return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                  size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(file, data, length, num_glyphs, kMaxClassDefValue);
+}
+
+bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t format = 0;
+  uint16_t mark_set_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&mark_set_count)) {
+    return OTS_FAILURE_MSG("Can' read mark glyph table structure");
+  }
+  if (format != 1) {
+    return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
+  }
+
+  const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
+  if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
+  }
+  for (unsigned i = 0; i < mark_set_count; ++i) {
+    uint32_t offset_coverage = 0;
+    if (!subtable.ReadU32(&offset_coverage)) {
+      return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
+    }
+    if (offset_coverage >= length ||
+        offset_coverage < mark_sets_end) {
+      return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
+    }
+  }
+  file->gdef->num_mark_glyph_sets = mark_set_count;
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gdef->data = 0; \
+    file->gdef->length = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in GDEF table.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  Buffer table(data, length);
+
+  OpenTypeGDEF *gdef = new OpenTypeGDEF;
+  file->gdef = gdef;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+  if (version < 0x00010000 || version == 0x00010001) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (version >= 0x00010002) {
+    gdef->version_2 = true;
+  }
+
+  uint16_t offset_glyph_class_def = 0;
+  uint16_t offset_attach_list = 0;
+  uint16_t offset_lig_caret_list = 0;
+  uint16_t offset_mark_attach_class_def = 0;
+  if (!table.ReadU16(&offset_glyph_class_def) ||
+      !table.ReadU16(&offset_attach_list) ||
+      !table.ReadU16(&offset_lig_caret_list) ||
+      !table.ReadU16(&offset_mark_attach_class_def)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+  uint16_t offset_mark_glyph_sets_def = 0;
+  if (gdef->version_2) {
+    if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
+      DROP_THIS_TABLE("Incomplete table");
+      return true;
+    }
+  }
+
+  unsigned gdef_header_end = 4 + 4 * 2;
+  if (gdef->version_2)
+    gdef_header_end += 2;
+
+  // Parse subtables
+  if (offset_glyph_class_def) {
+    if (offset_glyph_class_def >= length ||
+        offset_glyph_class_def < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to glyph classes");
+      return true;
+    }
+    if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def,
+                                 length - offset_glyph_class_def,
+                                 num_glyphs)) {
+      DROP_THIS_TABLE("Invalid glyph classes");
+      return true;
+    }
+    gdef->has_glyph_class_def = true;
+  }
+
+  if (offset_attach_list) {
+    if (offset_attach_list >= length ||
+        offset_attach_list < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to attachment list");
+      return true;
+    }
+    if (!ParseAttachListTable(file, data + offset_attach_list,
+                              length - offset_attach_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE("Invalid attachment list");
+      return true;
+    }
+  }
+
+  if (offset_lig_caret_list) {
+    if (offset_lig_caret_list >= length ||
+        offset_lig_caret_list < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to ligature caret list");
+      return true;
+    }
+    if (!ParseLigCaretListTable(file, data + offset_lig_caret_list,
+                              length - offset_lig_caret_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE("Invalid ligature caret list");
+      return true;
+    }
+  }
+
+  if (offset_mark_attach_class_def) {
+    if (offset_mark_attach_class_def >= length ||
+        offset_mark_attach_class_def < gdef_header_end) {
+      return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
+    }
+    if (!ParseMarkAttachClassDefTable(file,
+                                      data + offset_mark_attach_class_def,
+                                      length - offset_mark_attach_class_def,
+                                      num_glyphs)) {
+      DROP_THIS_TABLE("Invalid mark attachment list");
+      return true;
+    }
+    gdef->has_mark_attachment_class_def = true;
+  }
+
+  if (offset_mark_glyph_sets_def) {
+    if (offset_mark_glyph_sets_def >= length ||
+        offset_mark_glyph_sets_def < gdef_header_end) {
+      return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
+    }
+    if (!ParseMarkGlyphSetsDefTable(file,
+                                    data + offset_mark_glyph_sets_def,
+                                    length - offset_mark_glyph_sets_def,
+                                    num_glyphs)) {
+      DROP_THIS_TABLE("Invalid mark glyph sets");
+      return true;
+    }
+    gdef->has_mark_glyph_sets_def = true;
+  }
+  gdef->data = data;
+  gdef->length = length;
+  return true;
+}
+
+bool ots_gdef_should_serialise(OpenTypeFile *file) {
+  return file->gdef != NULL && file->gdef->data != NULL;
+}
+
+bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gdef->data, file->gdef->length)) {
+    return OTS_FAILURE_MSG("Failed to write GDEF table");
+  }
+
+  return true;
+}
+
+void ots_gdef_free(OpenTypeFile *file) {
+  delete file->gdef;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gdef.h b/third_party/ots/src/gdef.h
new file mode 100644
index 0000000..f46f419
--- /dev/null
+++ b/third_party/ots/src/gdef.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GDEF_H_
+#define OTS_GDEF_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGDEF {
+  OpenTypeGDEF()
+      : version_2(false),
+        has_glyph_class_def(false),
+        has_mark_attachment_class_def(false),
+        has_mark_glyph_sets_def(false),
+        num_mark_glyph_sets(0),
+        data(NULL),
+        length(0) {
+  }
+
+  bool version_2;
+  bool has_glyph_class_def;
+  bool has_mark_attachment_class_def;
+  bool has_mark_glyph_sets_def;
+  uint16_t num_mark_glyph_sets;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/glyf.cc b/third_party/ots/src/glyf.cc
new file mode 100644
index 0000000..3579397
--- /dev/null
+++ b/third_party/ots/src/glyf.cc
@@ -0,0 +1,298 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "glyf.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "head.h"
+#include "loca.h"
+#include "maxp.h"
+
+// glyf - Glyph Data
+// http://www.microsoft.com/typography/otspec/glyf.htm
+
+#define TABLE_NAME "glyf"
+
+namespace {
+
+bool ParseFlagsForSimpleGlyph(ots::OpenTypeFile *file,
+                              ots::Buffer *table,
+                              uint32_t gly_length,
+                              uint32_t num_flags,
+                              uint32_t *flags_count_logical,
+                              uint32_t *flags_count_physical,
+                              uint32_t *xy_coordinates_length) {
+  uint8_t flag = 0;
+  if (!table->ReadU8(&flag)) {
+    return OTS_FAILURE_MSG("Can't read flag");
+  }
+
+  uint32_t delta = 0;
+  if (flag & (1u << 1)) {  // x-Short
+    ++delta;
+  } else if (!(flag & (1u << 4))) {
+    delta += 2;
+  }
+
+  if (flag & (1u << 2)) {  // y-Short
+    ++delta;
+  } else if (!(flag & (1u << 5))) {
+    delta += 2;
+  }
+
+  if (flag & (1u << 3)) {  // repeat
+    if (*flags_count_logical + 1 >= num_flags) {
+      return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
+    }
+    uint8_t repeat = 0;
+    if (!table->ReadU8(&repeat)) {
+      return OTS_FAILURE_MSG("Can't read repeat value");
+    }
+    if (repeat == 0) {
+      return OTS_FAILURE_MSG("Zero repeat");
+    }
+    delta += (delta * repeat);
+
+    *flags_count_logical += repeat;
+    if (*flags_count_logical >= num_flags) {
+      return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
+    }
+    ++(*flags_count_physical);
+  }
+
+  if ((flag & (1u << 6)) || (flag & (1u << 7))) {  // reserved flags
+    return OTS_FAILURE_MSG("Bad flag value (%d)", flag);
+  }
+
+  *xy_coordinates_length += delta;
+  if (gly_length < *xy_coordinates_length) {
+    return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
+  }
+
+  return true;
+}
+
+bool ParseSimpleGlyph(ots::OpenTypeFile *file, const uint8_t *data,
+                      ots::Buffer *table, int16_t num_contours,
+                      uint32_t gly_offset, uint32_t gly_length,
+                      uint32_t *new_size) {
+  ots::OpenTypeGLYF *glyf = file->glyf;
+
+  // read the end-points array
+  uint16_t num_flags = 0;
+  for (int i = 0; i < num_contours; ++i) {
+    uint16_t tmp_index = 0;
+    if (!table->ReadU16(&tmp_index)) {
+      return OTS_FAILURE_MSG("Can't read contour index %d", i);
+    }
+    if (tmp_index == 0xffffu) {
+      return OTS_FAILURE_MSG("Bad contour index %d", i);
+    }
+    // check if the indices are monotonically increasing
+    if (i && (tmp_index + 1 <= num_flags)) {
+      return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
+    }
+    num_flags = tmp_index + 1;
+  }
+
+  uint16_t bytecode_length = 0;
+  if (!table->ReadU16(&bytecode_length)) {
+    return OTS_FAILURE_MSG("Can't read bytecode length");
+  }
+  if ((file->maxp->version_1) &&
+      (file->maxp->max_size_glyf_instructions < bytecode_length)) {
+    return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
+  }
+
+  const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
+  if (gly_length < (gly_header_length + bytecode_length)) {
+    return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
+  }
+
+  glyf->iov.push_back(std::make_pair(
+      data + gly_offset,
+      static_cast<size_t>(gly_header_length + bytecode_length)));
+
+  if (!table->Skip(bytecode_length)) {
+    return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
+  }
+
+  uint32_t flags_count_physical = 0;  // on memory
+  uint32_t xy_coordinates_length = 0;
+  for (uint32_t flags_count_logical = 0;
+       flags_count_logical < num_flags;
+       ++flags_count_logical, ++flags_count_physical) {
+    if (!ParseFlagsForSimpleGlyph(file,
+                                  table,
+                                  gly_length,
+                                  num_flags,
+                                  &flags_count_logical,
+                                  &flags_count_physical,
+                                  &xy_coordinates_length)) {
+      return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
+    }
+  }
+
+  if (gly_length < (gly_header_length + bytecode_length +
+                    flags_count_physical + xy_coordinates_length)) {
+    return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
+  }
+
+  if (gly_length - (gly_header_length + bytecode_length +
+                    flags_count_physical + xy_coordinates_length) > 3) {
+    // We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
+    // zero-padded length.
+    return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
+  }
+
+  glyf->iov.push_back(std::make_pair(
+      data + gly_offset + gly_header_length + bytecode_length,
+      static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
+
+  *new_size
+      = gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  if (!file->maxp || !file->loca || !file->head) {
+    return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
+  }
+
+  OpenTypeGLYF *glyf = new OpenTypeGLYF;
+  file->glyf = glyf;
+
+  const unsigned num_glyphs = file->maxp->num_glyphs;
+  std::vector<uint32_t> &offsets = file->loca->offsets;
+
+  if (offsets.size() != num_glyphs + 1) {
+    return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
+  }
+
+  std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
+  uint32_t current_offset = 0;
+
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    const unsigned gly_offset = offsets[i];
+    // The LOCA parser checks that these values are monotonic
+    const unsigned gly_length = offsets[i + 1] - offsets[i];
+    if (!gly_length) {
+      // this glyph has no outline (e.g. the space charactor)
+      resulting_offsets[i] = current_offset;
+      continue;
+    }
+
+    if (gly_offset >= length) {
+      return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
+    }
+    // Since these are unsigned types, the compiler is not allowed to assume
+    // that they never overflow.
+    if (gly_offset + gly_length < gly_offset) {
+      return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
+    }
+    if (gly_offset + gly_length > length) {
+      return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
+    }
+
+    table.set_offset(gly_offset);
+    int16_t num_contours, xmin, ymin, xmax, ymax;
+    if (!table.ReadS16(&num_contours) ||
+        !table.ReadS16(&xmin) ||
+        !table.ReadS16(&ymin) ||
+        !table.ReadS16(&xmax) ||
+        !table.ReadS16(&ymax)) {
+      return OTS_FAILURE_MSG("Can't read glyph %d header", i);
+    }
+
+    if (num_contours <= -2) {
+      // -2, -3, -4, ... are reserved for future use.
+      return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
+    }
+
+    // workaround for fonts in http://www.princexml.com/fonts/
+    if ((xmin == 32767) &&
+        (xmax == -32767) &&
+        (ymin == 32767) &&
+        (ymax == -32767)) {
+      OTS_WARNING("bad xmin/xmax/ymin/ymax values");
+      xmin = xmax = ymin = ymax = 0;
+    }
+
+    if (xmin > xmax || ymin > ymax) {
+      return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
+    }
+
+    unsigned new_size = 0;
+    if (num_contours >= 0) {
+      // this is a simple glyph and might contain bytecode
+      if (!ParseSimpleGlyph(file, data, &table,
+                            num_contours, gly_offset, gly_length, &new_size)) {
+        return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
+      }
+    } else {
+      // it's a composite glyph without any bytecode. Enqueue the whole thing
+      glyf->iov.push_back(std::make_pair(data + gly_offset,
+                                         static_cast<size_t>(gly_length)));
+      new_size = gly_length;
+    }
+
+    resulting_offsets[i] = current_offset;
+    // glyphs must be four byte aligned
+    // TODO(yusukes): investigate whether this padding is really necessary.
+    //                Which part of the spec requires this?
+    const unsigned padding = (4 - (new_size & 3)) % 4;
+    if (padding) {
+      glyf->iov.push_back(std::make_pair(
+          reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
+          static_cast<size_t>(padding)));
+      new_size += padding;
+    }
+    current_offset += new_size;
+  }
+  resulting_offsets[num_glyphs] = current_offset;
+
+  const uint16_t max16 = std::numeric_limits<uint16_t>::max();
+  if ((*std::max_element(resulting_offsets.begin(),
+                         resulting_offsets.end()) >= (max16 * 2u)) &&
+      (file->head->index_to_loc_format != 1)) {
+    OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
+    file->head->index_to_loc_format = 1;
+  }
+
+  file->loca->offsets = resulting_offsets;
+  return true;
+}
+
+bool ots_glyf_should_serialise(OpenTypeFile *file) {
+  return file->glyf != NULL;
+}
+
+bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGLYF *glyf = file->glyf;
+
+  for (unsigned i = 0; i < glyf->iov.size(); ++i) {
+    if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
+      return OTS_FAILURE_MSG("Falied to write glyph %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_glyf_free(OpenTypeFile *file) {
+  delete file->glyf;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/glyf.h b/third_party/ots/src/glyf.h
new file mode 100644
index 0000000..9a8baf5
--- /dev/null
+++ b/third_party/ots/src/glyf.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GLYF_H_
+#define OTS_GLYF_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGLYF {
+  std::vector<std::pair<const uint8_t*, size_t> > iov;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GLYF_H_
diff --git a/third_party/ots/src/gpos.cc b/third_party/ots/src/gpos.cc
new file mode 100644
index 0000000..a2b9687
--- /dev/null
+++ b/third_party/ots/src/gpos.cc
@@ -0,0 +1,828 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpos.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GPOS - The Glyph Positioning Table
+// http://www.microsoft.com/typography/otspec/gpos.htm
+
+#define TABLE_NAME "GPOS"
+
+namespace {
+
+enum GPOS_TYPE {
+  GPOS_TYPE_SINGLE_ADJUSTMENT = 1,
+  GPOS_TYPE_PAIR_ADJUSTMENT = 2,
+  GPOS_TYPE_CURSIVE_ATTACHMENT = 3,
+  GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4,
+  GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
+  GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
+  GPOS_TYPE_CONTEXT_POSITIONING = 7,
+  GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
+  GPOS_TYPE_EXTENSION_POSITIONING = 9,
+  GPOS_TYPE_RESERVED = 10
+};
+
+// The size of gpos header.
+const unsigned kGposHeaderSize = 10;
+// The maximum format number for anchor tables.
+const uint16_t kMaxAnchorFormat = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+// Lookup type parsers.
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length);
+bool ParsePairAdjustment(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length);
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length);
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length);
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length);
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
+  {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
+  {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
+  {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
+  {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
+  {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
+  {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
+  {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
+  {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
+  {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
+};
+
+const ots::LookupSubtableParser kGposLookupSubtableParser = {
+  arraysize(kGposTypeParsers),
+  GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
+};
+
+// Shared Tables: ValueRecord, Anchor Table, and MarkArray
+
+bool ParseValueRecord(const ots::OpenTypeFile *file,
+                      ots::Buffer* subtable, const uint8_t *data,
+                      const size_t length, const uint16_t value_format) {
+  // Check existence of adjustment fields.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> i) & 0x1) {
+      // Just read the field since these fileds could take an arbitrary values.
+      if (!subtable->Skip(2)) {
+        return OTS_FAILURE_MSG("Failed to read value reacord component");
+      }
+    }
+  }
+
+  // Check existence of offsets to device table.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> (i + 4)) & 0x1) {
+      uint16_t offset = 0;
+      if (!subtable->ReadU16(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read value record offset");
+      }
+      if (offset) {
+        // TODO(bashi): Is it possible that device tables locate before
+        // this record? No fonts contain such offset AKAIF.
+        if (offset >= length) {
+          return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length);
+        }
+        if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
+          return OTS_FAILURE_MSG("Failed to parse device table in value record");
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseAnchorTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  // Read format and skip 2 2-byte fields that could be arbitrary values.
+  if (!subtable.ReadU16(&format) ||
+      !subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Faled to read anchor table");
+  }
+
+  if (format == 0 || format > kMaxAnchorFormat) {
+    return OTS_FAILURE_MSG("Bad Anchor table format %d", format);
+  }
+
+  // Format 2 and 3 has additional fields.
+  if (format == 2) {
+    // Format 2 provides an index to a glyph contour point, which will take
+    // arbitrary value.
+    uint16_t anchor_point = 0;
+    if (!subtable.ReadU16(&anchor_point)) {
+      return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table");
+    }
+  } else if (format == 3) {
+    uint16_t offset_x_device = 0;
+    uint16_t offset_y_device = 0;
+    if (!subtable.ReadU16(&offset_x_device) ||
+        !subtable.ReadU16(&offset_y_device)) {
+      return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table");
+    }
+    const unsigned format_end = static_cast<unsigned>(10);
+    if (offset_x_device) {
+      if (offset_x_device < format_end || offset_x_device >= length) {
+        return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device);
+      }
+      if (!ots::ParseDeviceTable(file, data + offset_x_device,
+                                 length - offset_x_device)) {
+        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+      }
+    }
+    if (offset_y_device) {
+      if (offset_y_device < format_end || offset_y_device >= length) {
+        return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device);
+      }
+      if (!ots::ParseDeviceTable(file, data + offset_y_device,
+                                 length - offset_y_device)) {
+        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkArrayTable(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t mark_count = 0;
+  if (!subtable.ReadU16(&mark_count)) {
+    return OTS_FAILURE_MSG("Can't read mark table length");
+  }
+
+  // MarkRecord consists of 4-bytes.
+  const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2;
+  if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark table length");
+  }
+  for (unsigned i = 0; i < mark_count; ++i) {
+    uint16_t class_value = 0;
+    uint16_t offset_mark_anchor = 0;
+    if (!subtable.ReadU16(&class_value) ||
+        !subtable.ReadU16(&offset_mark_anchor)) {
+      return OTS_FAILURE_MSG("Can't read mark table %d", i);
+    }
+    // |class_value| may take arbitrary values including 0 here so we don't
+    // check the value.
+    if (offset_mark_anchor < mark_records_end ||
+        offset_mark_anchor >= length) {
+      return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i);
+    }
+    if (!ParseAnchorTable(file, data + offset_mark_anchor,
+                          length - offset_mark_anchor)) {
+      return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 1:
+// Single Adjustment Positioning Subtable
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                           const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format)) {
+    return OTS_FAILURE_MSG("Can't read single adjustment information");
+  }
+
+  if (format == 1) {
+    // Format 1 exactly one value record.
+    if (!ParseValueRecord(file, &subtable, data, length, value_format)) {
+      return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
+    }
+  } else if (format == 2) {
+    uint16_t value_count = 0;
+    if (!subtable.ReadU16(&value_count)) {
+      return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
+    }
+    for (unsigned i = 0; i < value_count; ++i) {
+      if (!ParseValueRecord(file, &subtable, data, length, value_format)) {
+        return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
+      }
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage);
+  }
+
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
+  }
+
+  return true;
+}
+
+bool ParsePairSetTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t value_format1,
+                       const uint16_t value_format2,
+                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t value_count = 0;
+  if (!subtable.ReadU16(&value_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair set table structure");
+  }
+  for (unsigned i = 0; i < value_count; ++i) {
+    // Check pair value record.
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i);
+    }
+    if (glyph_id >= num_glyphs) {
+      return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
+    }
+    if (!ParseValueRecord(file, &subtable, data, length, value_format1)) {
+      return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
+    }
+    if (!ParseValueRecord(file, &subtable, data, length, value_format2)) {
+      return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
+    }
+  }
+  return true;
+}
+
+bool ParsePairPosFormat1(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos table structure");
+  }
+
+  uint16_t pair_set_count = 0;
+  if (!subtable.ReadU16(&pair_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos set count");
+  }
+
+  const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10;
+  if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end);
+  }
+  for (unsigned i = 0; i < pair_set_count; ++i) {
+    uint16_t pair_set_offset = 0;
+    if (!subtable.ReadU16(&pair_set_offset)) {
+      return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i);
+    }
+    if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
+      return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i);
+    }
+    // Check pair set tables
+    if (!ParsePairSetTable(file, data + pair_set_offset, length - pair_set_offset,
+                           value_format1, value_format2,
+                           num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair set table %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParsePairPosFormat2(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure");
+  }
+
+  uint16_t offset_class_def1 = 0;
+  uint16_t offset_class_def2 = 0;
+  uint16_t class1_count = 0;
+  uint16_t class2_count = 0;
+  if (!subtable.ReadU16(&offset_class_def1) ||
+      !subtable.ReadU16(&offset_class_def2) ||
+      !subtable.ReadU16(&class1_count) ||
+      !subtable.ReadU16(&class2_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
+  }
+
+  // Check class 1 records.
+  for (unsigned i = 0; i < class1_count; ++i) {
+    // Check class 2 records.
+    for (unsigned j = 0; j < class2_count; ++j) {
+      if (value_format1 && !ParseValueRecord(file, &subtable, data, length,
+                                             value_format1)) {
+        return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
+      }
+      if (value_format2 && !ParseValueRecord(file, &subtable, data, length,
+                                             value_format2)) {
+        return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
+      }
+    }
+  }
+
+  // Check class definition tables.
+  if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
+      offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
+    return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def1,
+                               length - offset_class_def1,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table 1");
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def2,
+                               length - offset_class_def2,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table 2");
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Pair Adjustment Positioning Subtable
+bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                         const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format1 = 0;
+  uint16_t value_format2 = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format1) ||
+      !subtable.ReadU16(&value_format2)) {
+    return OTS_FAILURE_MSG("Failed to read pair adjustment structure");
+  }
+
+  if (format == 1) {
+    if (!ParsePairPosFormat1(file, data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
+    }
+  } else if (format == 2) {
+    if (!ParsePairPosFormat2(file, data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair format 2");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad pos pair format %d", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+// Lookup Type 3
+// Cursive Attachment Positioning Subtable
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data,
+                            const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t entry_exit_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&entry_exit_count)) {
+    return OTS_FAILURE_MSG("Failed to read cursive attachment structure");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad cursive attachment format %d", format);
+  }
+
+  // Check entry exit records.
+  const unsigned entry_exit_records_end =
+      2 * static_cast<unsigned>(entry_exit_count) + 6;
+  if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end);
+  }
+  for (unsigned i = 0; i < entry_exit_count; ++i) {
+    uint16_t offset_entry_anchor = 0;
+    uint16_t offset_exit_anchor = 0;
+    if (!subtable.ReadU16(&offset_entry_anchor) ||
+        !subtable.ReadU16(&offset_exit_anchor)) {
+      return OTS_FAILURE_MSG("Can't read entry exit record %d", i);
+    }
+    // These offsets could be NULL.
+    if (offset_entry_anchor) {
+      if (offset_entry_anchor < entry_exit_records_end ||
+          offset_entry_anchor >= length) {
+        return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
+      }
+      if (!ParseAnchorTable(file, data + offset_entry_anchor,
+                            length - offset_entry_anchor)) {
+        return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i);
+      }
+    }
+    if (offset_exit_anchor) {
+      if (offset_exit_anchor < entry_exit_records_end ||
+         offset_exit_anchor >= length) {
+        return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
+      }
+      if (!ParseAnchorTable(file, data + offset_exit_anchor,
+                            length - offset_exit_anchor)) {
+        return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i);
+      }
+    }
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
+  }
+
+  return true;
+}
+
+bool ParseAnchorArrayTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t record_count = 0;
+  if (!subtable.ReadU16(&record_count)) {
+    return OTS_FAILURE_MSG("Can't read anchor array length");
+  }
+
+  const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) *
+      static_cast<unsigned>(class_count) + 2;
+  if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end);
+  }
+  for (unsigned i = 0; i < record_count; ++i) {
+    for (unsigned j = 0; j < class_count; ++j) {
+      uint16_t offset_record = 0;
+      if (!subtable.ReadU16(&offset_record)) {
+        return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i);
+      }
+      // |offset_record| could be NULL.
+      if (offset_record) {
+        if (offset_record < anchor_array_end || offset_record >= length) {
+          return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i);
+        }
+        if (!ParseAnchorTable(file, data + offset_record,
+                              length - offset_record)) {
+          return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i);
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseLigatureArrayTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length,
+                             const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligature count");
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature_attach = 0;
+    if (!subtable.ReadU16(&offset_ligature_attach)) {
+      return OTS_FAILURE_MSG("Can't read ligature offset %d", i);
+    }
+    if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
+      return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i);
+    }
+    if (!ParseAnchorArrayTable(file, data + offset_ligature_attach,
+                               length - offset_ligature_attach, class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i);
+    }
+  }
+  return true;
+}
+
+// Common parser for Lookup Type 4, 5 and 6.
+bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length,
+                                    const GPOS_TYPE type) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage1 = 0;
+  uint16_t offset_coverage2 = 0;
+  uint16_t class_count = 0;
+  uint16_t offset_mark_array = 0;
+  uint16_t offset_type_specific_array = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage1) ||
+      !subtable.ReadU16(&offset_coverage2) ||
+      !subtable.ReadU16(&class_count) ||
+      !subtable.ReadU16(&offset_mark_array) ||
+      !subtable.ReadU16(&offset_type_specific_array)) {
+    return OTS_FAILURE_MSG("Failed to read mark attachment subtable header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format);
+  }
+
+  const unsigned header_end = static_cast<unsigned>(subtable.offset());
+  if (header_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end);
+  }
+  if (offset_coverage1 < header_end || offset_coverage1 >= length) {
+    return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage1,
+                               length - offset_coverage1,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse converge 1 table");
+  }
+  if (offset_coverage2 < header_end || offset_coverage2 >= length) {
+    return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage2,
+                               length - offset_coverage2,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table 2");
+  }
+
+  if (offset_mark_array < header_end || offset_mark_array >= length) {
+    return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array);
+  }
+  if (!ParseMarkArrayTable(file, data + offset_mark_array,
+                           length - offset_mark_array, class_count)) {
+    return OTS_FAILURE_MSG("Failed to parse mark array");
+  }
+
+  if (offset_type_specific_array < header_end ||
+      offset_type_specific_array >= length) {
+    return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array);
+  }
+  if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
+      type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
+    if (!ParseAnchorArrayTable(file, data + offset_type_specific_array,
+                               length - offset_type_specific_array,
+                               class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse anchor array");
+    }
+  } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
+    if (!ParseLigatureArrayTable(file, data + offset_type_specific_array,
+                                 length - offset_type_specific_array,
+                                 class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature array");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad attachment type %d", type);
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// MarkToBase Attachment Positioning Subtable
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
+}
+
+// Lookup Type 5:
+// MarkToLigature Attachment Positioning Subtable
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
+}
+
+// Lookup Type 6:
+// MarkToMark Attachment Positioning Subtable
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
+}
+
+// Lookup Type 7:
+// Contextual Positioning Subtables
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
+                                   file->gpos->num_lookups);
+}
+
+// Lookup Type 8:
+// Chaining Contexual Positioning Subtable
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length) {
+  return ots::ParseChainingContextSubtable(file, data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gpos->num_lookups);
+}
+
+// Lookup Type 9:
+// Extension Positioning
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGposLookupSubtableParser);
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gpos->data = 0; \
+    file->gpos->length = 0; \
+  } while (0)
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid GPOS table and
+// OTS will drop their GPOS table.
+//
+// # invalid delta format in device table
+// samanata.ttf
+//
+// # bad size range in device table
+// Sarai_07.ttf
+//
+// # bad offset to PairSetTable
+// chandas1-2.ttf
+//
+// # bad offset to FeatureTable
+// glrso12.ttf
+// gllr12.ttf
+// glbo12.ttf
+// glb12.ttf
+// glro12.ttf
+// glbso12.ttf
+// glrc12.ttf
+// glrsc12.ttf
+// glbs12.ttf
+// glrs12.ttf
+// glr12.ttf
+//
+// # ScriptRecords aren't sorted by tag
+// Garogier_unhinted.otf
+//
+// # bad start coverage index in CoverageFormat2
+// AndBasR.ttf
+// CharisSILB.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILR.ttf
+// DoulosSILR.ttf
+// GenBasBI.ttf
+// GenBasI.ttf
+// GenBkBasI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// Padauk-Bold.ttf
+// Padauk.ttf
+//
+// # Contour point indexes aren't sorted
+// Arial Unicode.ttf
+
+bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing GPOS table requires num_glyphs which is contained in maxp table.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
+  }
+
+  Buffer table(data, length);
+
+  OpenTypeGPOS *gpos = new OpenTypeGPOS;
+  file->gpos = gpos;
+
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
+
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGposLookupSubtableParser,
+                              &gpos->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
+  }
+
+  uint16_t num_features = 0;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gpos->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
+  }
+
+  if (offset_script_list) {
+    if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
+  }
+
+  gpos->data = data;
+  gpos->length = length;
+  return true;
+}
+
+bool ots_gpos_should_serialise(OpenTypeFile *file) {
+  return file->gpos != NULL && file->gpos->data != NULL;
+}
+
+bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gpos->data, file->gpos->length)) {
+    return OTS_FAILURE_MSG("Failed to write GPOS table");
+  }
+
+  return true;
+}
+
+void ots_gpos_free(OpenTypeFile *file) {
+  delete file->gpos;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gpos.h b/third_party/ots/src/gpos.h
new file mode 100644
index 0000000..3a4034f
--- /dev/null
+++ b/third_party/ots/src/gpos.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GPOS_H_
+#define OTS_GPOS_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGPOS {
+  OpenTypeGPOS()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPOS table
+  uint16_t num_lookups;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/gsub.cc b/third_party/ots/src/gsub.cc
new file mode 100644
index 0000000..af31144
--- /dev/null
+++ b/third_party/ots/src/gsub.cc
@@ -0,0 +1,685 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gsub.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GSUB - The Glyph Substitution Table
+// http://www.microsoft.com/typography/otspec/gsub.htm
+
+#define TABLE_NAME "GSUB"
+
+namespace {
+
+// The GSUB header size
+const size_t kGsubHeaderSize = 4 + 3 * 2;
+
+enum GSUB_TYPE {
+  GSUB_TYPE_SINGLE = 1,
+  GSUB_TYPE_MULTIPLE = 2,
+  GSUB_TYPE_ALTERNATE = 3,
+  GSUB_TYPE_LIGATURE = 4,
+  GSUB_TYPE_CONTEXT = 5,
+  GSUB_TYPE_CHANGING_CONTEXT = 6,
+  GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
+  GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
+  GSUB_TYPE_RESERVED = 9
+};
+
+// Lookup type parsers.
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+      const uint8_t *data, const size_t length);
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length);
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
+  {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
+  {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
+  {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
+  {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
+  {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
+  {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
+  {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
+  {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
+    ParseReverseChainingContextSingleSubstitution}
+};
+
+const ots::LookupSubtableParser kGsubLookupSubtableParser = {
+  arraysize(kGsubTypeParsers),
+  GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
+};
+
+// Lookup Type 1:
+// Single Substitution Subtable
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE_MSG("Failed to read single subst table header");
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  if (format == 1) {
+    // Parse SingleSubstFormat1
+    int16_t delta_glyph_id = 0;
+    if (!subtable.ReadS16(&delta_glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
+    }
+    if (std::abs(delta_glyph_id) >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
+    }
+  } else if (format == 2) {
+    // Parse SingleSubstFormat2
+    uint16_t glyph_count = 0;
+    if (!subtable.ReadU16(&glyph_count)) {
+      return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table");
+    }
+    if (glyph_count > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
+    }
+    for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t substitute = 0;
+      if (!subtable.ReadU16(&substitute)) {
+        return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i);
+      }
+      if (substitute >= num_glyphs) {
+        return OTS_FAILURE_MSG("too large substitute: %u", substitute);
+      }
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad single subst table format %d", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseSequenceTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i);
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Multiple Substitution Subtable
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE_MSG("Can't read header of multiple subst table");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned sequence_end = static_cast<unsigned>(6) +
+      sequence_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end);
+  }
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    uint16_t offset_sequence = 0;
+    if (!subtable.ReadU16(&offset_sequence)) {
+      return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i);
+    }
+    if (offset_sequence < sequence_end || offset_sequence >= length) {
+      return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i);
+    }
+    if (!ParseSequenceTable(file, data + offset_sequence, length - offset_sequence,
+                            num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse sequence table %d", i);
+    }
+  }
+
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseAlternateSetTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read alternate set header");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t alternate = 0;
+    if (!subtable.ReadU16(&alternate)) {
+      return OTS_FAILURE_MSG("Can't read alternate %d", i);
+    }
+    if (alternate >= num_glyphs) {
+      return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
+    }
+  }
+  return true;
+}
+
+// Lookup Type 3:
+// Alternate Substitution Subtable
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t alternate_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&alternate_set_count)) {
+    return OTS_FAILURE_MSG("Can't read alternate subst header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned alternate_set_end = static_cast<unsigned>(6) +
+      alternate_set_count * 2;
+  if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
+  }
+  for (unsigned i = 0; i < alternate_set_count; ++i) {
+    uint16_t offset_alternate_set = 0;
+    if (!subtable.ReadU16(&offset_alternate_set)) {
+      return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i);
+    }
+    if (offset_alternate_set < alternate_set_end ||
+        offset_alternate_set >= length) {
+      return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i);
+    }
+    if (!ParseAlternateSetTable(file, data + offset_alternate_set,
+                                length - offset_alternate_set,
+                                num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse alternate set");
+    }
+  }
+
+  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseLigatureTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lig_glyph = 0;
+  uint16_t comp_count = 0;
+
+  if (!subtable.ReadU16(&lig_glyph) ||
+      !subtable.ReadU16(&comp_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligatuer table header");
+  }
+
+  if (lig_glyph >= num_glyphs) {
+    return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
+  }
+  if (comp_count == 0 || comp_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad component count of %d", comp_count);
+  }
+  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
+    uint16_t component = 0;
+    if (!subtable.ReadU16(&component)) {
+      return OTS_FAILURE_MSG("Can't read ligature component %d", i);
+    }
+    if (component >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
+    }
+  }
+
+  return true;
+}
+
+bool ParseLigatureSetTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
+  }
+
+  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
+  if (ligature_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature = 0;
+    if (!subtable.ReadU16(&offset_ligature)) {
+      return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
+    }
+    if (offset_ligature < ligature_end || offset_ligature >= length) {
+      return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
+    }
+    if (!ParseLigatureTable(file, data + offset_ligature, length - offset_ligature,
+                            num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// Ligature Substitution Subtable
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t lig_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligature substitution header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned ligature_set_end = static_cast<unsigned>(6) +
+      lig_set_count * 2;
+  if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
+  }
+  for (unsigned i = 0; i < lig_set_count; ++i) {
+    uint16_t offset_ligature_set = 0;
+    if (!subtable.ReadU16(&offset_ligature_set)) {
+      return OTS_FAILURE_MSG("Can't read ligature set offset %d", i);
+    }
+    if (offset_ligature_set < ligature_set_end ||
+        offset_ligature_set >= length) {
+      return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i);
+    }
+    if (!ParseLigatureSetTable(file, data + offset_ligature_set,
+                               length - offset_ligature_set, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature set %d", i);
+    }
+  }
+
+  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+// Lookup Type 5:
+// Contextual Substitution Subtable
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
+                                   file->gsub->num_lookups);
+}
+
+// Lookup Type 6:
+// Chaining Contextual Substitution Subtable
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length) {
+  return ots::ParseChainingContextSubtable(file, data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gsub->num_lookups);
+}
+
+// Lookup Type 7:
+// Extension Substition
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGsubLookupSubtableParser);
+}
+
+// Lookup Type 8:
+// Reverse Chaining Contexual Single Substitution Subtable
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE_MSG("Failed to read reverse chaining header");
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  uint16_t backtrack_glyph_count = 0;
+  if (!subtable.ReadU16(&backtrack_glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
+  }
+  if (backtrack_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count);
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_glyph_count);
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i);
+    }
+    offsets_backtrack.push_back(offset);
+  }
+
+  uint16_t lookahead_glyph_count = 0;
+  if (!subtable.ReadU16(&lookahead_glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read look ahead glyph count");
+  }
+  if (lookahead_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count);
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_glyph_count);
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Can't read look ahead offset %d", i);
+    }
+    offsets_lookahead.push_back(offset);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i);
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
+    }
+  }
+
+  const unsigned substitute_end = static_cast<unsigned>(10) +
+      (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
+  if (substitute_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table");
+  }
+
+  if (offset_coverage < substitute_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table");
+  }
+
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    if (offsets_backtrack[i] < substitute_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    if (offsets_lookahead[i] < substitute_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gsub->data = 0; \
+    file->gsub->length = 0; \
+  } while (0)
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid values in GSUB table.
+// OTS will drop their GSUB table.
+//
+// # too large substitute (value is 0xFFFF)
+// kaiu.ttf
+// mingliub2.ttf
+// mingliub1.ttf
+// mingliub0.ttf
+// GraublauWeb.otf
+// GraublauWebBold.otf
+//
+// # too large alternate (value is 0xFFFF)
+// ManchuFont.ttf
+//
+// # bad offset to lang sys table (NULL offset)
+// DejaVuMonoSansBold.ttf
+// DejaVuMonoSansBoldOblique.ttf
+// DejaVuMonoSansOblique.ttf
+// DejaVuSansMono-BoldOblique.ttf
+// DejaVuSansMono-Oblique.ttf
+// DejaVuSansMono-Bold.ttf
+//
+// # bad start coverage index
+// GenBasBI.ttf
+// GenBasI.ttf
+// AndBasR.ttf
+// GenBkBasI.ttf
+// CharisSILR.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILB.ttf
+// DoulosSILR.ttf
+// CharisSILBI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// GenBkBasBI.ttf
+// GenBasB.ttf
+// GenBasR.ttf
+//
+// # glyph range is overlapping
+// KacstTitleL.ttf
+// KacstDecorative.ttf
+// KacstTitle.ttf
+// KacstArt.ttf
+// KacstPoster.ttf
+// KacstQurn.ttf
+// KacstDigital.ttf
+// KacstBook.ttf
+// KacstFarsi.ttf
+
+bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing gsub table requires |file->maxp->num_glyphs|
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
+  }
+
+  Buffer table(data, length);
+
+  OpenTypeGSUB *gsub = new OpenTypeGSUB;
+  file->gsub = gsub;
+
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
+
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGsubLookupSubtableParser,
+                              &gsub->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
+  }
+
+  uint16_t num_features = 0;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gsub->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
+  }
+
+  if (offset_script_list) {
+    if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
+  }
+
+  gsub->data = data;
+  gsub->length = length;
+  return true;
+}
+
+bool ots_gsub_should_serialise(OpenTypeFile *file) {
+  return file->gsub != NULL && file->gsub->data != NULL;
+}
+
+bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gsub->data, file->gsub->length)) {
+    return OTS_FAILURE_MSG("Failed to write GSUB table");
+  }
+
+  return true;
+}
+
+void ots_gsub_free(OpenTypeFile *file) {
+  delete file->gsub;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gsub.h b/third_party/ots/src/gsub.h
new file mode 100644
index 0000000..f6f8cd3
--- /dev/null
+++ b/third_party/ots/src/gsub.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GSUB_H_
+#define OTS_GSUB_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGSUB {
+  OpenTypeGSUB()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPSUB table
+  uint16_t num_lookups;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GSUB_H_
+
diff --git a/third_party/ots/src/hdmx.cc b/third_party/ots/src/hdmx.cc
new file mode 100644
index 0000000..098802b
--- /dev/null
+++ b/third_party/ots/src/hdmx.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hdmx.h"
+#include "head.h"
+#include "maxp.h"
+
+// hdmx - Horizontal Device Metrics
+// http://www.microsoft.com/typography/otspec/hdmx.htm
+
+#define TABLE_NAME "hdmx"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->hdmx; \
+    file->hdmx = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_hdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->hdmx = new OpenTypeHDMX;
+  OpenTypeHDMX * const hdmx = file->hdmx;
+
+  if (!file->head || !file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
+  }
+
+  if ((file->head->flags & 0x14) == 0) {
+    // http://www.microsoft.com/typography/otspec/recom.htm
+    DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
+                    "head->flags are not set");
+    return true;
+  }
+
+  int16_t num_recs;
+  if (!table.ReadU16(&hdmx->version) ||
+      !table.ReadS16(&num_recs) ||
+      !table.ReadS32(&hdmx->size_device_record)) {
+    return OTS_FAILURE_MSG("Failed to read hdmx header");
+  }
+  if (hdmx->version != 0) {
+    DROP_THIS_TABLE("bad version: %u", hdmx->version);
+    return true;
+  }
+  if (num_recs <= 0) {
+    DROP_THIS_TABLE("bad num_recs: %d", num_recs);
+    return true;
+  }
+  const int32_t actual_size_device_record = file->maxp->num_glyphs + 2;
+  if (hdmx->size_device_record < actual_size_device_record) {
+    DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
+    return true;
+  }
+
+  hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
+  if (hdmx->pad_len > 3) {
+    return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
+  }
+
+  uint8_t last_pixel_size = 0;
+  hdmx->records.reserve(num_recs);
+  for (int i = 0; i < num_recs; ++i) {
+    OpenTypeHDMXDeviceRecord rec;
+
+    if (!table.ReadU8(&rec.pixel_size) ||
+        !table.ReadU8(&rec.max_width)) {
+      return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
+    }
+    if ((i != 0) &&
+        (rec.pixel_size <= last_pixel_size)) {
+      DROP_THIS_TABLE("records are not sorted");
+      return true;
+    }
+    last_pixel_size = rec.pixel_size;
+
+    rec.widths.reserve(file->maxp->num_glyphs);
+    for (unsigned j = 0; j < file->maxp->num_glyphs; ++j) {
+      uint8_t width;
+      if (!table.ReadU8(&width)) {
+        return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
+      }
+      rec.widths.push_back(width);
+    }
+
+    if ((hdmx->pad_len > 0) &&
+        !table.Skip(hdmx->pad_len)) {
+      return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
+    }
+
+    hdmx->records.push_back(rec);
+  }
+
+  return true;
+}
+
+bool ots_hdmx_should_serialise(OpenTypeFile *file) {
+  if (!file->hdmx) return false;
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return true;
+}
+
+bool ots_hdmx_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeHDMX * const hdmx = file->hdmx;
+
+  const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
+  if (hdmx->records.size() >
+          static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
+      !out->WriteU16(hdmx->version) ||
+      !out->WriteS16(num_recs) ||
+      !out->WriteS32(hdmx->size_device_record)) {
+    return OTS_FAILURE_MSG("Failed to write hdmx header");
+  }
+
+  for (int16_t i = 0; i < num_recs; ++i) {
+    const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
+    if (!out->Write(&rec.pixel_size, 1) ||
+        !out->Write(&rec.max_width, 1) ||
+        !out->Write(&rec.widths[0], rec.widths.size())) {
+      return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
+    }
+    if ((hdmx->pad_len > 0) &&
+        !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
+      return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
+    }
+  }
+
+  return true;
+}
+
+void ots_hdmx_free(OpenTypeFile *file) {
+  delete file->hdmx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/hdmx.h b/third_party/ots/src/hdmx.h
new file mode 100644
index 0000000..9ec2124
--- /dev/null
+++ b/third_party/ots/src/hdmx.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HDMX_H_
+#define OTS_HDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHDMXDeviceRecord {
+  uint8_t pixel_size;
+  uint8_t max_width;
+  std::vector<uint8_t> widths;
+};
+
+struct OpenTypeHDMX {
+  uint16_t version;
+  int32_t size_device_record;
+  int32_t pad_len;
+  std::vector<OpenTypeHDMXDeviceRecord> records;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/third_party/ots/src/head.cc b/third_party/ots/src/head.cc
new file mode 100644
index 0000000..dcd234d
--- /dev/null
+++ b/third_party/ots/src/head.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "head.h"
+
+#include <cstring>
+
+// head - Font Header
+// http://www.microsoft.com/typography/otspec/head.htm
+
+#define TABLE_NAME "head"
+
+namespace ots {
+
+bool ots_head_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->head = new OpenTypeHEAD;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU32(&file->head->revision)) {
+    return OTS_FAILURE_MSG("Failed to read head header");
+  }
+
+  if (version >> 16 != 1) {
+    return OTS_FAILURE_MSG("Bad head table version of %d", version);
+  }
+
+  // Skip the checksum adjustment
+  if (!table.Skip(4)) {
+    return OTS_FAILURE_MSG("Failed to read checksum");
+  }
+
+  uint32_t magic;
+  if (!table.ReadTag(&magic) ||
+      std::memcmp(&magic, "\x5F\x0F\x3C\xF5", 4)) {
+    return OTS_FAILURE_MSG("Failed to read font magic number");
+  }
+
+  if (!table.ReadU16(&file->head->flags)) {
+    return OTS_FAILURE_MSG("Failed to read head flags");
+  }
+
+  // We allow bits 0..4, 11..13
+  file->head->flags &= 0x381f;
+
+  if (!table.ReadU16(&file->head->ppem)) {
+    return OTS_FAILURE_MSG("Failed to read pixels per em");
+  }
+
+  // ppem must be in range
+  if (file->head->ppem < 16 ||
+      file->head->ppem > 16384) {
+    return OTS_FAILURE_MSG("Bad ppm of %d", file->head->ppem);
+  }
+
+  // ppem must be a power of two
+#if 0
+  // We don't call ots_failure() for now since lots of TrueType fonts are
+  // not following this rule. Putting OTS_WARNING here is too noisy.
+  if ((file->head->ppem - 1) & file->head->ppem) {
+    return OTS_FAILURE_MSG("ppm not a power of two: %d", file->head->ppem);
+  }
+#endif
+
+  if (!table.ReadR64(&file->head->created) ||
+      !table.ReadR64(&file->head->modified)) {
+    return OTS_FAILURE_MSG("Can't read font dates");
+  }
+
+  if (!table.ReadS16(&file->head->xmin) ||
+      !table.ReadS16(&file->head->ymin) ||
+      !table.ReadS16(&file->head->xmax) ||
+      !table.ReadS16(&file->head->ymax)) {
+    return OTS_FAILURE_MSG("Failed to read font bounding box");
+  }
+
+  if (file->head->xmin > file->head->xmax) {
+    return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", file->head->xmin, file->head->xmax);
+  }
+  if (file->head->ymin > file->head->ymax) {
+    return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", file->head->ymin, file->head->ymax);
+  }
+
+  if (!table.ReadU16(&file->head->mac_style)) {
+    return OTS_FAILURE_MSG("Failed to read font style");
+  }
+
+  // We allow bits 0..6
+  file->head->mac_style &= 0x7f;
+
+  if (!table.ReadU16(&file->head->min_ppem)) {
+    return OTS_FAILURE_MSG("Failed to read font minimum ppm");
+  }
+
+  // We don't care about the font direction hint
+  if (!table.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip font direction hint");
+  }
+
+  if (!table.ReadS16(&file->head->index_to_loc_format)) {
+    return OTS_FAILURE_MSG("Failed to read index to loc format");
+  }
+  if (file->head->index_to_loc_format < 0 ||
+      file->head->index_to_loc_format > 1) {
+    return OTS_FAILURE_MSG("Bad index to loc format %d", file->head->index_to_loc_format);
+  }
+
+  int16_t glyph_data_format;
+  if (!table.ReadS16(&glyph_data_format) ||
+      glyph_data_format) {
+    return OTS_FAILURE_MSG("Failed to read glyph data format");
+  }
+
+  return true;
+}
+
+bool ots_head_should_serialise(OpenTypeFile *file) {
+  return file->head != NULL;
+}
+
+bool ots_head_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->WriteU32(0x00010000) ||
+      !out->WriteU32(file->head->revision) ||
+      !out->WriteU32(0) ||  // check sum not filled in yet
+      !out->WriteU32(0x5F0F3CF5) ||
+      !out->WriteU16(file->head->flags) ||
+      !out->WriteU16(file->head->ppem) ||
+      !out->WriteR64(file->head->created) ||
+      !out->WriteR64(file->head->modified) ||
+      !out->WriteS16(file->head->xmin) ||
+      !out->WriteS16(file->head->ymin) ||
+      !out->WriteS16(file->head->xmax) ||
+      !out->WriteS16(file->head->ymax) ||
+      !out->WriteU16(file->head->mac_style) ||
+      !out->WriteU16(file->head->min_ppem) ||
+      !out->WriteS16(2) ||
+      !out->WriteS16(file->head->index_to_loc_format) ||
+      !out->WriteS16(0)) {
+    return OTS_FAILURE_MSG("Failed to write head table");
+  }
+
+  return true;
+}
+
+void ots_head_free(OpenTypeFile *file) {
+  delete file->head;
+}
+
+}  // namespace
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/head.h b/third_party/ots/src/head.h
new file mode 100644
index 0000000..5967c4b
--- /dev/null
+++ b/third_party/ots/src/head.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HEAD_H_
+#define OTS_HEAD_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHEAD {
+  uint32_t revision;
+  uint16_t flags;
+  uint16_t ppem;
+  uint64_t created;
+  uint64_t modified;
+
+  int16_t xmin, xmax;
+  int16_t ymin, ymax;
+
+  uint16_t mac_style;
+  uint16_t min_ppem;
+  int16_t index_to_loc_format;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HEAD_H_
diff --git a/third_party/ots/src/hhea.cc b/third_party/ots/src/hhea.cc
new file mode 100644
index 0000000..8430442
--- /dev/null
+++ b/third_party/ots/src/hhea.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hhea.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// hhea - Horizontal Header
+// http://www.microsoft.com/typography/otspec/hhea.htm
+
+#define TABLE_NAME "hhea"
+
+namespace ots {
+
+bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeHHEA *hhea = new OpenTypeHHEA;
+  file->hhea = hhea;
+
+  if (!table.ReadU32(&hhea->header.version)) {
+    return OTS_FAILURE_MSG("Failed to read hhea version");
+  }
+  if (hhea->header.version >> 16 != 1) {
+    return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version);
+  }
+
+  if (!ParseMetricsHeader(file, &table, &hhea->header)) {
+    return OTS_FAILURE_MSG("Failed to parse horizontal metrics");
+  }
+
+  return true;
+}
+
+bool ots_hhea_should_serialise(OpenTypeFile *file) {
+  return file->hhea != NULL;
+}
+
+bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(file, out, &file->hhea->header)) {
+    return OTS_FAILURE_MSG("Failed to serialise horizontal metrics");
+  }
+  return true;
+}
+
+void ots_hhea_free(OpenTypeFile *file) {
+  delete file->hhea;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/hhea.h b/third_party/ots/src/hhea.h
new file mode 100644
index 0000000..bdea9aa
--- /dev/null
+++ b/third_party/ots/src/hhea.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HHEA_H_
+#define OTS_HHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HHEA_H_
diff --git a/third_party/ots/src/hmtx.cc b/third_party/ots/src/hmtx.cc
new file mode 100644
index 0000000..ae86513
--- /dev/null
+++ b/third_party/ots/src/hmtx.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hmtx.h"
+
+#include "hhea.h"
+#include "maxp.h"
+
+// hmtx - Horizontal Metrics
+// http://www.microsoft.com/typography/otspec/hmtx.htm
+
+#define TABLE_NAME "hmtx"
+
+namespace ots {
+
+bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeHMTX *hmtx = new OpenTypeHMTX;
+  file->hmtx = hmtx;
+
+  if (!file->hhea || !file->maxp) {
+    return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx");
+  }
+
+  if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs,
+                         &file->hhea->header, &hmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to parse hmtx metrics");
+  }
+
+  return true;
+}
+
+bool ots_hmtx_should_serialise(OpenTypeFile *file) {
+  return file->hmtx != NULL;
+}
+
+bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(file, out, &file->hmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to serialise htmx metrics");
+  }
+  return true;
+}
+
+void ots_hmtx_free(OpenTypeFile *file) {
+  delete file->hmtx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/hmtx.h b/third_party/ots/src/hmtx.h
new file mode 100644
index 0000000..435949c
--- /dev/null
+++ b/third_party/ots/src/hmtx.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HMTX_H_
+#define OTS_HMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HMTX_H_
diff --git a/third_party/ots/src/kern.cc b/third_party/ots/src/kern.cc
new file mode 100644
index 0000000..744c057
--- /dev/null
+++ b/third_party/ots/src/kern.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "kern.h"
+
+// kern - Kerning
+// http://www.microsoft.com/typography/otspec/kern.htm
+
+#define TABLE_NAME "kern"
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    delete file->kern; \
+    file->kern = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_kern_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeKERN *kern = new OpenTypeKERN;
+  file->kern = kern;
+
+  uint16_t num_tables = 0;
+  if (!table.ReadU16(&kern->version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE_MSG("Failed to read kern header");
+  }
+
+  if (kern->version > 0) {
+    DROP_THIS_TABLE("bad table version");
+    return true;
+  }
+
+  if (num_tables == 0) {
+    DROP_THIS_TABLE("num_tables is zero");
+    return true;
+  }
+
+  kern->subtables.reserve(num_tables);
+  for (unsigned i = 0; i < num_tables; ++i) {
+    OpenTypeKERNFormat0 subtable;
+    uint16_t sub_length = 0;
+
+    if (!table.ReadU16(&subtable.version) ||
+        !table.ReadU16(&sub_length)) {
+      return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
+    }
+
+    if (subtable.version > 0) {
+      OTS_WARNING("Bad subtable version: %d", subtable.version);
+      continue;
+    }
+
+    const size_t current_offset = table.offset();
+    if (current_offset - 4 + sub_length > length) {
+      return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
+    }
+
+    if (!table.ReadU16(&subtable.coverage)) {
+      return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
+    }
+
+    if (!(subtable.coverage & 0x1)) {
+      OTS_WARNING(
+          "We don't support vertical data as the renderer doesn't support it.");
+      continue;
+    }
+    if (subtable.coverage & 0xF0) {
+      DROP_THIS_TABLE("Reserved fields should zero-filled.");
+      return true;
+    }
+    const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
+    if (format != 0) {
+      OTS_WARNING("Format %d is not supported.", format);
+      continue;
+    }
+
+    // Parse the format 0 field.
+    uint16_t num_pairs = 0;
+    if (!table.ReadU16(&num_pairs) ||
+        !table.ReadU16(&subtable.search_range) ||
+        !table.ReadU16(&subtable.entry_selector) ||
+        !table.ReadU16(&subtable.range_shift)) {
+      return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
+    }
+
+    if (!num_pairs) {
+      DROP_THIS_TABLE("Zero length subtable is found.");
+      return true;
+    }
+
+    // Sanity checks for search_range, entry_selector, and range_shift. See the
+    // comment in ots.cc for details.
+    const size_t kFormat0PairSize = 6;  // left, right, and value. 2 bytes each.
+    if (num_pairs > (65536 / kFormat0PairSize)) {
+      // Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
+      DROP_THIS_TABLE("Too large subtable.");
+      return true;
+    }
+    unsigned max_pow2 = 0;
+    while (1u << (max_pow2 + 1) <= num_pairs) {
+      ++max_pow2;
+    }
+    const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
+    if (subtable.search_range != expected_search_range) {
+      OTS_WARNING("bad search range");
+      subtable.search_range = expected_search_range;
+    }
+    if (subtable.entry_selector != max_pow2) {
+      return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
+    }
+    const uint16_t expected_range_shift =
+        kFormat0PairSize * num_pairs - subtable.search_range;
+    if (subtable.range_shift != expected_range_shift) {
+      OTS_WARNING("bad range shift");
+      subtable.range_shift = expected_range_shift;
+    }
+
+    // Read kerning pairs.
+    subtable.pairs.reserve(num_pairs);
+    uint32_t last_pair = 0;
+    for (unsigned j = 0; j < num_pairs; ++j) {
+      OpenTypeKERNFormat0Pair kerning_pair;
+      if (!table.ReadU16(&kerning_pair.left) ||
+          !table.ReadU16(&kerning_pair.right) ||
+          !table.ReadS16(&kerning_pair.value)) {
+        return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
+      }
+      const uint32_t current_pair
+          = (kerning_pair.left << 16) + kerning_pair.right;
+      if (j != 0 && current_pair <= last_pair) {
+        // Many free fonts don't follow this rule, so we don't call OTS_FAILURE
+        // in order to support these fonts.
+        DROP_THIS_TABLE("Kerning pairs are not sorted.");
+        return true;
+      }
+      last_pair = current_pair;
+      subtable.pairs.push_back(kerning_pair);
+    }
+
+    kern->subtables.push_back(subtable);
+  }
+
+  if (!kern->subtables.size()) {
+    DROP_THIS_TABLE("All subtables are removed.");
+    return true;
+  }
+
+  return true;
+}
+
+bool ots_kern_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->kern != NULL;
+}
+
+bool ots_kern_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeKERN *kern = file->kern;
+
+  const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
+  if (num_subtables != kern->subtables.size() ||
+      !out->WriteU16(kern->version) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE_MSG("Can't write kern table header");
+  }
+
+  for (uint16_t i = 0; i < num_subtables; ++i) {
+    const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
+    if (length > std::numeric_limits<uint16_t>::max() ||
+        !out->WriteU16(kern->subtables[i].version) ||
+        !out->WriteU16(static_cast<uint16_t>(length)) ||
+        !out->WriteU16(kern->subtables[i].coverage) ||
+        !out->WriteU16(
+            static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
+        !out->WriteU16(kern->subtables[i].search_range) ||
+        !out->WriteU16(kern->subtables[i].entry_selector) ||
+        !out->WriteU16(kern->subtables[i].range_shift)) {
+      return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
+    }
+    for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
+      if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
+          !out->WriteU16(kern->subtables[i].pairs[j].right) ||
+          !out->WriteS16(kern->subtables[i].pairs[j].value)) {
+        return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_kern_free(OpenTypeFile *file) {
+  delete file->kern;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/kern.h b/third_party/ots/src/kern.h
new file mode 100644
index 0000000..9350ef7
--- /dev/null
+++ b/third_party/ots/src/kern.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_KERN_H_
+#define OTS_KERN_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeKERNFormat0Pair {
+  uint16_t left;
+  uint16_t right;
+  int16_t value;
+};
+
+struct OpenTypeKERNFormat0 {
+  uint16_t version;
+  uint16_t coverage;
+  uint16_t search_range;
+  uint16_t entry_selector;
+  uint16_t range_shift;
+  std::vector<OpenTypeKERNFormat0Pair> pairs;
+};
+
+// Format 2 is not supported. Since the format is not supported by Windows,
+// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
+// free fonts, and found no font uses the format.
+
+struct OpenTypeKERN {
+  uint16_t version;
+  std::vector<OpenTypeKERNFormat0> subtables;
+};
+
+}  // namespace ots
+
+#endif  // OTS_KERN_H_
diff --git a/third_party/ots/src/layout.cc b/third_party/ots/src/layout.cc
new file mode 100644
index 0000000..856152c
--- /dev/null
+++ b/third_party/ots/src/layout.cc
@@ -0,0 +1,1511 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "layout.h"
+
+#include <limits>
+#include <vector>
+
+#include "gdef.h"
+
+// OpenType Layout Common Table Formats
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+#define TABLE_NAME "Layout" // XXX: use individual table names
+
+namespace {
+
+// The 'DFLT' tag of script table.
+const uint32_t kScriptTableTagDflt = 0x44464c54;
+// The value which represents there is no required feature index.
+const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
+// The lookup flag bit which indicates existence of MarkFilteringSet.
+const uint16_t kUseMarkFilteringSetBit = 0x0010;
+// The lookup flags which require GDEF table.
+const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
+// The mask for MarkAttachmentType.
+const uint16_t kMarkAttachmentTypeMask = 0xFF00;
+// The maximum type number of format for device tables.
+const uint16_t kMaxDeltaFormatType = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+struct ScriptRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct LangSysRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct FeatureRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+bool ParseLangSysTable(const ots::OpenTypeFile *file,
+                       ots::Buffer *subtable, const uint32_t tag,
+                       const uint16_t num_features) {
+  uint16_t offset_lookup_order = 0;
+  uint16_t req_feature_index = 0;
+  uint16_t feature_count = 0;
+  if (!subtable->ReadU16(&offset_lookup_order) ||
+      !subtable->ReadU16(&req_feature_index) ||
+      !subtable->ReadU16(&feature_count)) {
+    return OTS_FAILURE_MSG("Failed to read langsys header for tag %4.4s", (char *)&tag);
+  }
+  // |offset_lookup_order| is reserved and should be NULL.
+  if (offset_lookup_order != 0) {
+    return OTS_FAILURE_MSG("Bad lookup offset order %d for langsys tag %4.4s", offset_lookup_order, (char *)&tag);
+  }
+  if (req_feature_index != kNoRequiredFeatureIndexDefined &&
+      req_feature_index >= num_features) {
+    return OTS_FAILURE_MSG("Bad required features index %d for langsys tag %4.4s", req_feature_index, (char *)&tag);
+  }
+  if (feature_count > num_features) {
+    return OTS_FAILURE_MSG("Bad feature count %d for langsys tag %4.4s", feature_count, (char *)&tag);
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    uint16_t feature_index = 0;
+    if (!subtable->ReadU16(&feature_index)) {
+      return OTS_FAILURE_MSG("Failed to read feature index %d for langsys tag %4.4s", i, (char *)&tag);
+    }
+    if (feature_index >= num_features) {
+      return OTS_FAILURE_MSG("Bad feature index %d for feature %d for langsys tag %4.4s", feature_index, i, (char *)&tag);
+    }
+  }
+  return true;
+}
+
+bool ParseScriptTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, const size_t length,
+                      const uint32_t tag, const uint16_t num_features) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_default_lang_sys = 0;
+  uint16_t lang_sys_count = 0;
+  if (!subtable.ReadU16(&offset_default_lang_sys) ||
+      !subtable.ReadU16(&lang_sys_count)) {
+    return OTS_FAILURE_MSG("Failed to read script header for script tag %4.4s", (char *)&tag);
+  }
+
+  // The spec requires a script table for 'DFLT' tag must contain non-NULL
+  // |offset_default_lang_sys| and |lang_sys_count| == 0
+  if (tag == kScriptTableTagDflt &&
+      (offset_default_lang_sys == 0 || lang_sys_count != 0)) {
+    return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. for script tag %4.4s", (char *)&tag);
+  }
+
+  const unsigned lang_sys_record_end =
+      6 * static_cast<unsigned>(lang_sys_count) + 4;
+  if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of langsys record %d for script tag %4.4s", lang_sys_record_end, (char *)&tag);
+  }
+
+  std::vector<LangSysRecord> lang_sys_records;
+  lang_sys_records.resize(lang_sys_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
+        !subtable.ReadU16(&lang_sys_records[i].offset)) {
+      return OTS_FAILURE_MSG("Failed to read langsys record header %d for script tag %4.4s", i, (char *)&tag);
+    }
+    // The record array must store the records alphabetically by tag
+    if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
+      return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d for script tag %4.4s", last_tag, i, (char *)&tag);
+    }
+    if (lang_sys_records[i].offset < lang_sys_record_end ||
+        lang_sys_records[i].offset >= length) {
+      return OTS_FAILURE_MSG("bad offset to lang sys table: %x",
+                  lang_sys_records[i].offset);
+    }
+    last_tag = lang_sys_records[i].tag;
+  }
+
+  // Check lang sys tables
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    subtable.set_offset(lang_sys_records[i].offset);
+    if (!ParseLangSysTable(file, &subtable, lang_sys_records[i].tag, num_features)) {
+      return OTS_FAILURE_MSG("Failed to parse langsys table %d (%4.4s) for script tag %4.4s", i, (char *)&lang_sys_records[i].tag, (char *)&tag);
+    }
+  }
+
+  return true;
+}
+
+bool ParseFeatureTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_feature_params = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&offset_feature_params) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read feature table header");
+  }
+
+  const unsigned feature_table_end =
+      2 * static_cast<unsigned>(lookup_count) + 4;
+  if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of feature table %d", feature_table_end);
+  }
+  // |offset_feature_params| is generally set to NULL.
+  if (offset_feature_params != 0 &&
+      (offset_feature_params < feature_table_end ||
+       offset_feature_params >= length)) {
+    return OTS_FAILURE_MSG("Bad feature params offset %d", offset_feature_params);
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    uint16_t lookup_index = 0;
+    if (!subtable.ReadU16(&lookup_index)) {
+      return OTS_FAILURE_MSG("Failed to read lookup index for lookup %d", i);
+    }
+    // lookup index starts with 0.
+    if (lookup_index >= num_lookups) {
+      return OTS_FAILURE_MSG("Bad lookup index %d for lookup %d", lookup_index, i);
+    }
+  }
+  return true;
+}
+
+bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data,
+                      const size_t length,
+                      const ots::LookupSubtableParser* parser) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lookup_type = 0;
+  uint16_t lookup_flag = 0;
+  uint16_t subtable_count = 0;
+  if (!subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU16(&lookup_flag) ||
+      !subtable.ReadU16(&subtable_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup table header");
+  }
+
+  if (lookup_type == 0 || lookup_type > parser->num_types) {
+    return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
+  }
+
+  // Check lookup flags.
+  if ((lookup_flag & kGdefRequiredFlags) &&
+      (!file->gdef || !file->gdef->has_glyph_class_def)) {
+    return OTS_FAILURE_MSG("Bad lookup flags %d", lookup_flag);
+  }
+  if ((lookup_flag & kMarkAttachmentTypeMask) &&
+      (!file->gdef || !file->gdef->has_mark_attachment_class_def)) {
+    return OTS_FAILURE_MSG("lookup flag asks for mark attachment that is bad %d", lookup_flag);
+  }
+  bool use_mark_filtering_set = false;
+  if (lookup_flag & kUseMarkFilteringSetBit) {
+    if (!file->gdef || !file->gdef->has_mark_glyph_sets_def) {
+      return OTS_FAILURE_MSG("lookup flag asks for mark filtering that is bad %d", lookup_flag);
+    }
+    use_mark_filtering_set = true;
+  }
+
+  std::vector<uint16_t> subtables;
+  subtables.reserve(subtable_count);
+  // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
+  // extra 2 bytes will follow after subtable offset array.
+  const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) +
+      (use_mark_filtering_set ? 8 : 6);
+  if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup %d", lookup_table_end);
+  }
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    uint16_t offset_subtable = 0;
+    if (!subtable.ReadU16(&offset_subtable)) {
+      return OTS_FAILURE_MSG("Failed to read subtable offset %d", i);
+    }
+    if (offset_subtable < lookup_table_end ||
+        offset_subtable >= length) {
+      return OTS_FAILURE_MSG("Bad subtable offset %d for subtable %d", offset_subtable, i);
+    }
+    subtables.push_back(offset_subtable);
+  }
+  if (subtables.size() != subtable_count) {
+    return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size());
+  }
+
+  if (use_mark_filtering_set) {
+    uint16_t mark_filtering_set = 0;
+    if (!subtable.ReadU16(&mark_filtering_set)) {
+      return OTS_FAILURE_MSG("Failed to read mark filtering set");
+    }
+    if (file->gdef->num_mark_glyph_sets == 0 ||
+        mark_filtering_set >= file->gdef->num_mark_glyph_sets) {
+      return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
+    }
+  }
+
+  // Parse lookup subtables for this lookup type.
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    if (!parser->Parse(file, data + subtables[i], length - subtables[i],
+                       lookup_type)) {
+      return OTS_FAILURE_MSG("Failed to parse subtable %d", i);
+    }
+  }
+  return true;
+}
+
+bool ParseClassDefFormat1(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip class definition header");
+  }
+
+  uint16_t start_glyph = 0;
+  if (!subtable.ReadU16(&start_glyph)) {
+    return OTS_FAILURE_MSG("Failed to read starting glyph of class definition");
+  }
+  if (start_glyph > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad starting glyph %d in class definition", start_glyph);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in class definition");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE_MSG("Failed to read class value for glyph %d in class definition", i);
+    }
+    if (class_value > num_classes) {
+      return OTS_FAILURE_MSG("Bad class value %d for glyph %d in class definition", class_value, i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefFormat2(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE_MSG("Failed to read range count in class definition");
+  }
+  if (range_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad range count: %u", range_count);
+  }
+
+  uint16_t last_end = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
+    }
+    if (start > end || (last_end && start <= last_end)) {
+      return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
+    }
+    if (class_value > num_classes) {
+      return OTS_FAILURE_MSG("bad class value: %u", class_value);
+    }
+    last_end = end;
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat1(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t expected_num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip coverage format");
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in coverage");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph)) {
+      return OTS_FAILURE_MSG("Failed to read glyph %d in coverage", i);
+    }
+    if (glyph > num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+  }
+
+  if (expected_num_glyphs && expected_num_glyphs != glyph_count) {
+      return OTS_FAILURE_MSG("unexpected number of glyphs: %u", glyph_count);
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat2(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t expected_num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip format of coverage type 2");
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE_MSG("Failed to read range count in coverage");
+  }
+  if (range_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad range count: %u", range_count);
+  }
+  uint16_t last_end = 0;
+  uint16_t last_start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t start_coverage_index = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&start_coverage_index)) {
+      return OTS_FAILURE_MSG("Failed to read range %d in coverage", i);
+    }
+
+    // Some of the Adobe Pro fonts have ranges that overlap by one element: the
+    // start of one range is equal to the end of the previous range. Therefore
+    // the < in the following condition should be <= were it not for this.
+    // See crbug.com/134135.
+    if (start > end || (last_end && start < last_end)) {
+      return OTS_FAILURE_MSG("glyph range is overlapping.");
+    }
+    if (start_coverage_index != last_start_coverage_index) {
+      return OTS_FAILURE_MSG("bad start coverage index.");
+    }
+    last_end = end;
+    last_start_coverage_index += end - start + 1;
+  }
+
+  if (expected_num_glyphs &&
+      expected_num_glyphs != last_start_coverage_index) {
+      return OTS_FAILURE_MSG("unexpected number of glyphs: %u", last_start_coverage_index);
+  }
+
+  return true;
+}
+
+// Parsers for Contextual subtables in GSUB/GPOS tables.
+
+bool ParseLookupRecord(const ots::OpenTypeFile *file,
+                       ots::Buffer *subtable, const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  uint16_t sequence_index = 0;
+  uint16_t lookup_list_index = 0;
+  if (!subtable->ReadU16(&sequence_index) ||
+      !subtable->ReadU16(&lookup_list_index)) {
+    return OTS_FAILURE_MSG("Failed to read header for lookup record");
+  }
+  if (sequence_index >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad sequence index %d in lookup record", sequence_index);
+  }
+  if (lookup_list_index >= num_lookups) {
+    return OTS_FAILURE_MSG("Bad lookup list index %d in lookup record", lookup_list_index);
+  }
+  return true;
+}
+
+bool ParseRuleSubtable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule subtable header");
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in rule subtable", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph %d", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph %d for entry %d", glyph_id, i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d", i);
+    }
+  }
+  return true;
+}
+
+bool ParseRuleSetTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t rule_count = 0;
+  if (!subtable.ReadU16(&rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in rule set");
+  }
+  const unsigned rule_end = 2 * static_cast<unsigned>(rule_count) + 2;
+  if (rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of rule %d in rule set", rule_end);
+  }
+
+  for (unsigned i = 0; i < rule_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE_MSG("Failed to read rule offset for rule set %d", i);
+    }
+    if (offset_rule < rule_end || offset_rule >= length) {
+      return OTS_FAILURE_MSG("Bad rule offset %d in set %d", offset_rule, i);
+    }
+    if (!ParseRuleSubtable(file, data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse rule set %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat1(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&rule_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of context format 1");
+  }
+
+  const unsigned rule_set_end = static_cast<unsigned>(6) +
+      rule_set_count * 2;
+  if (rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of rule set %d of context format 1", rule_set_end);
+  }
+  if (offset_coverage < rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in context format 1", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in context format 1");
+  }
+
+  for (unsigned i = 0; i < rule_set_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE_MSG("Failed to read rule offset %d in context format 1", i);
+    }
+    if (offset_rule < rule_set_end || offset_rule >= length) {
+      return OTS_FAILURE_MSG("Bad rule offset %d in rule %d in context format 1", offset_rule, i);
+    }
+    if (!ParseRuleSetTable(file, data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse rule set %d in context format 1", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassRuleTable(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of class rule table");
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in class rule table", glyph_count);
+  }
+
+  // ClassRule table contains an array of classes. Each value of classes
+  // could take arbitrary values including zero so we don't check these value.
+  const unsigned num_classes = glyph_count - static_cast<unsigned>(1);
+  if (!subtable.Skip(2 * num_classes)) {
+    return OTS_FAILURE_MSG("Failed to skip classes in class rule table");
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in class rule table", i);
+    }
+  }
+  return true;
+}
+
+bool ParseClassSetTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t class_rule_count = 0;
+  if (!subtable.ReadU16(&class_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read class rule count in class set table");
+  }
+  const unsigned class_rule_end =
+      2 * static_cast<unsigned>(class_rule_count) + 2;
+  if (class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("bad class rule end %d in class set table", class_rule_end);
+  }
+  for (unsigned i = 0; i < class_rule_count; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read class rule offset %d in class set table", i);
+    }
+    if (offset_class_rule < class_rule_end || offset_class_rule >= length) {
+      return OTS_FAILURE_MSG("Bad class rule offset %d in class %d", offset_class_rule, i);
+    }
+    if (!ParseClassRuleTable(file, data + offset_class_rule,
+                             length - offset_class_rule, num_glyphs,
+                             num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse class rule table %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat2(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_class_def = 0;
+  uint16_t class_set_cnt = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_class_def) ||
+      !subtable.ReadU16(&class_set_cnt)) {
+    return OTS_FAILURE_MSG("Failed to read header for context format 2");
+  }
+
+  const unsigned class_set_end = 2 * static_cast<unsigned>(class_set_cnt) + 8;
+  if (class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of class set %d for context format 2", class_set_end);
+  }
+  if (offset_coverage < class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in context format 2", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in context format 2");
+  }
+
+  if (offset_class_def < class_set_end || offset_class_def >= length) {
+    return OTS_FAILURE_MSG("bad class definition offset %d in context format 2", offset_class_def);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def,
+                               length - offset_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
+  }
+
+  for (unsigned i = 0; i < class_set_cnt; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read class rule offset %d in context format 2", i);
+    }
+    if (offset_class_rule) {
+      if (offset_class_rule < class_set_end || offset_class_rule >= length) {
+        return OTS_FAILURE_MSG("Bad class rule offset %d for rule %d in context format 2", offset_class_rule, i);
+      }
+      if (!ParseClassSetTable(file, data + offset_class_rule,
+                              length - offset_class_rule, num_glyphs,
+                              num_lookups)) {
+        return OTS_FAILURE_MSG("Failed to parse class set %d in context format 2", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat3(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read header in context format 3");
+  }
+
+  if (glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in context format 3", glyph_count);
+  }
+  const unsigned lookup_record_end = 2 * static_cast<unsigned>(glyph_count) +
+      4 * static_cast<unsigned>(lookup_count) + 6;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup %d in context format 3", lookup_record_end);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t offset_coverage = 0;
+    if (!subtable.ReadU16(&offset_coverage)) {
+      return OTS_FAILURE_MSG("Failed to read coverage offset %d in conxtext format 3", i);
+    }
+    if (offset_coverage < lookup_record_end || offset_coverage >= length) {
+      return OTS_FAILURE_MSG("Bad coverage offset %d for glyph %d in context format 3", offset_coverage, i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for glyph %d in context format 3", i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in context format 3", i);
+    }
+  }
+
+  return true;
+}
+
+// Parsers for Chaning Contextual subtables in GSUB/GPOS tables.
+
+bool ParseChainRuleSubtable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain rule subtable");
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain rule subtable", backtrack_count);
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for bactrack glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain rule subtable");
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain rule subtable", input_count);
+  }
+  for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read input glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for input glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookahead count in chain rule subtable");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain rule subtable", lookahead_count);
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read lookahead glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for lookadhead glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain rule subtable");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain rule subtable", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainRuleSetTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_rule_count = 0;
+  if (!subtable.ReadU16(&chain_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in chain rule set");
+  }
+  const unsigned chain_rule_end =
+      2 * static_cast<unsigned>(chain_rule_count) + 2;
+  if (chain_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of chain rule %d in chain rule set", chain_rule_end);
+  }
+  for (unsigned i = 0; i < chain_rule_count; ++i) {
+    uint16_t offset_chain_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_rule)) {
+      return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain rule set", i);
+    }
+    if (offset_chain_rule < chain_rule_end || offset_chain_rule >= length) {
+      return OTS_FAILURE_MSG("Bad chain rule offset %d for chain rule %d in chain rule set", offset_chain_rule, i);
+    }
+    if (!ParseChainRuleSubtable(file, data + offset_chain_rule,
+                                length - offset_chain_rule,
+                                num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain rule %d in chain rule set", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat1(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t chain_rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&chain_rule_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of chain context format 1");
+  }
+
+  const unsigned chain_rule_set_end =
+      2 * static_cast<unsigned>(chain_rule_set_count) + 6;
+  if (chain_rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad chain rule end %d in chain context format 1", chain_rule_set_end);
+  }
+  if (offset_coverage < chain_rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 1", chain_rule_set_end);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table for chain context format 1");
+  }
+
+  for (unsigned i = 0; i < chain_rule_set_count; ++i) {
+    uint16_t offset_chain_rule_set = 0;
+    if (!subtable.ReadU16(&offset_chain_rule_set)) {
+      return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain context format 1", i);
+    }
+    if (offset_chain_rule_set < chain_rule_set_end ||
+        offset_chain_rule_set >= length) {
+      return OTS_FAILURE_MSG("Bad chain rule set offset %d for chain rule set %d in chain context format 1", offset_chain_rule_set, i);
+    }
+    if (!ParseChainRuleSetTable(file, data + offset_chain_rule_set,
+                                   length - offset_chain_rule_set,
+                                   num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain rule set %d in chain context format 1", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassRuleSubtable(const ots::OpenTypeFile *file,
+                                 const uint8_t *data, const size_t length,
+                                 const uint16_t num_glyphs,
+                                 const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  // In this subtable, we don't check the value of classes for now since
+  // these could take arbitrary values.
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain class rule subtable");
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain class rule subtable", backtrack_count);
+  }
+  if (!subtable.Skip(2 * backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to skip backtrack offsets in chain class rule subtable");
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain class rule subtable");
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain class rule subtable", input_count);
+  }
+  if (!subtable.Skip(2 * (input_count - 1))) {
+    return OTS_FAILURE_MSG("Failed to skip input offsets in chain class rule subtable");
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookahead count in chain class rule subtable");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain class rule subtable", lookahead_count);
+  }
+  if (!subtable.Skip(2 * lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to skip lookahead offsets in chain class rule subtable");
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain class rule subtable");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain class rule subtable", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassSetTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length,
+                             const uint16_t num_glyphs,
+                             const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_class_rule_count = 0;
+  if (!subtable.ReadU16(&chain_class_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in chain class set");
+  }
+  const unsigned chain_class_rule_end =
+      2 * static_cast<unsigned>(chain_class_rule_count) + 2;
+  if (chain_class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of chain class set %d in chain class set", chain_class_rule_end);
+  }
+  for (unsigned i = 0; i < chain_class_rule_count; ++i) {
+    uint16_t offset_chain_class_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read chain class rule offset %d in chain class set", i);
+    }
+    if (offset_chain_class_rule < chain_class_rule_end ||
+        offset_chain_class_rule >= length) {
+      return OTS_FAILURE_MSG("Bad chain class rule offset %d for chain class %d in chain class set", offset_chain_class_rule, i);
+    }
+    if (!ParseChainClassRuleSubtable(file, data + offset_chain_class_rule,
+                                     length - offset_chain_class_rule,
+                                     num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain class rule %d in chain class set", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat2(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_backtrack_class_def = 0;
+  uint16_t offset_input_class_def = 0;
+  uint16_t offset_lookahead_class_def = 0;
+  uint16_t chain_class_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_backtrack_class_def) ||
+      !subtable.ReadU16(&offset_input_class_def) ||
+      !subtable.ReadU16(&offset_lookahead_class_def) ||
+      !subtable.ReadU16(&chain_class_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of chain context format 2");
+  }
+
+  const unsigned chain_class_set_end =
+      2 * static_cast<unsigned>(chain_class_set_count) + 12;
+  if (chain_class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad chain class set end %d in chain context format 2", chain_class_set_end);
+  }
+  if (offset_coverage < chain_class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 2", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in chain context format 2");
+  }
+
+  // Classes for backtrack/lookahead sequences might not be defined.
+  if (offset_backtrack_class_def) {
+    if (offset_backtrack_class_def < chain_class_set_end ||
+        offset_backtrack_class_def >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack class offset %d in chain context format 2", offset_backtrack_class_def);
+    }
+    if (!ots::ParseClassDefTable(file, data + offset_backtrack_class_def,
+                                 length - offset_backtrack_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
+    }
+  }
+
+  if (offset_input_class_def < chain_class_set_end ||
+      offset_input_class_def >= length) {
+    return OTS_FAILURE_MSG("Bad input class defn offset %d in chain context format 2", offset_input_class_def);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_input_class_def,
+                               length - offset_input_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
+  }
+
+  if (offset_lookahead_class_def) {
+    if (offset_lookahead_class_def < chain_class_set_end ||
+        offset_lookahead_class_def >= length) {
+      return OTS_FAILURE_MSG("Bad lookahead class defn offset %d in chain context format 2", offset_lookahead_class_def);
+    }
+    if (!ots::ParseClassDefTable(file, data + offset_lookahead_class_def,
+                                 length - offset_lookahead_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
+    }
+  }
+
+  for (unsigned i = 0; i < chain_class_set_count; ++i) {
+    uint16_t offset_chain_class_set = 0;
+    if (!subtable.ReadU16(&offset_chain_class_set)) {
+      return OTS_FAILURE_MSG("Failed to read chain class set offset %d", i);
+    }
+    // |offset_chain_class_set| could be NULL.
+    if (offset_chain_class_set) {
+      if (offset_chain_class_set < chain_class_set_end ||
+          offset_chain_class_set >= length) {
+        return OTS_FAILURE_MSG("Bad chain set class offset %d for chain set %d in chain context format 2", offset_chain_class_set, i);
+      }
+      if (!ParseChainClassSetTable(file, data + offset_chain_class_set,
+                                   length - offset_chain_class_set,
+                                   num_glyphs, num_lookups)) {
+        return OTS_FAILURE_MSG("Failed to parse chain class set table %d in chain context format 2", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat3(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain context format 3");
+  }
+
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain context format 3", backtrack_count);
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_count);
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack offset %d in chain context format 3", i);
+    }
+    offsets_backtrack.push_back(offset);
+  }
+  if (offsets_backtrack.size() != backtrack_count) {
+    return OTS_FAILURE_MSG("Bad backtrack offsets size %ld in chain context format 3", offsets_backtrack.size());
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain context format 3");
+  }
+  if (input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain context format 3", input_count);
+  }
+  std::vector<uint16_t> offsets_input;
+  offsets_input.reserve(input_count);
+  for (unsigned i = 0; i < input_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read input offset %d in chain context format 3", i);
+    }
+    offsets_input.push_back(offset);
+  }
+  if (offsets_input.size() != input_count) {
+    return OTS_FAILURE_MSG("Bad input offsets size %ld in chain context format 3", offsets_input.size());
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed ot read lookahead count in chain context format 3");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain context format 3", lookahead_count);
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_count);
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read lookahead offset %d in chain context format 3", i);
+    }
+    offsets_lookahead.push_back(offset);
+  }
+  if (offsets_lookahead.size() != lookahead_count) {
+    return OTS_FAILURE_MSG("Bad lookahead offsets size %ld in chain context format 3", offsets_lookahead.size());
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain context format 3");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup %d in chain context format 3", i);
+    }
+  }
+
+  const unsigned lookup_record_end =
+      2 * (static_cast<unsigned>(backtrack_count) +
+           static_cast<unsigned>(input_count) +
+           static_cast<unsigned>(lookahead_count)) +
+      4 * static_cast<unsigned>(lookup_count) + 10;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup record %d in chain context format 3", lookup_record_end);
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    if (offsets_backtrack[i] < lookup_record_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack offset of %d for backtrack %d in chain context format 3", offsets_backtrack[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse backtrack coverage %d in chain context format 3", i);
+    }
+  }
+  for (unsigned i = 0; i < input_count; ++i) {
+    if (offsets_input[i] < lookup_record_end || offsets_input[i] >= length) {
+      return OTS_FAILURE_MSG("Bad input offset %d for input %d in chain context format 3", offsets_input[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_input[i],
+                                 length - offsets_input[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse input coverage table %d in chain context format 3", i);
+    }
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    if (offsets_lookahead[i] < lookup_record_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE_MSG("Bad lookadhead offset %d for lookahead %d in chain context format 3", offsets_lookahead[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in chain context format 3", i);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool LookupSubtableParser::Parse(const OpenTypeFile *file, const uint8_t *data,
+                                 const size_t length,
+                                 const uint16_t lookup_type) const {
+  for (unsigned i = 0; i < num_types; ++i) {
+    if (parsers[i].type == lookup_type && parsers[i].parse) {
+      if (!parsers[i].parse(file, data, length)) {
+        return OTS_FAILURE_MSG("Failed to parse lookup subtable %d", i);
+      }
+      return true;
+    }
+  }
+  return OTS_FAILURE_MSG("No lookup subtables to parse");
+}
+
+// Parsing ScriptListTable requires number of features so we need to
+// parse FeatureListTable before calling this function.
+bool ParseScriptListTable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t script_count = 0;
+  if (!subtable.ReadU16(&script_count)) {
+    return OTS_FAILURE_MSG("Failed to read script count in script list table");
+  }
+
+  const unsigned script_record_end =
+      6 * static_cast<unsigned>(script_count) + 2;
+  if (script_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of script record %d in script list table", script_record_end);
+  }
+  std::vector<ScriptRecord> script_list;
+  script_list.reserve(script_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < script_count; ++i) {
+    ScriptRecord record;
+    if (!subtable.ReadU32(&record.tag) ||
+        !subtable.ReadU16(&record.offset)) {
+      return OTS_FAILURE_MSG("Failed to read script record %d in script list table", i);
+    }
+    // Script tags should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > record.tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = record.tag;
+    if (record.offset < script_record_end || record.offset >= length) {
+      return OTS_FAILURE_MSG("Bad record offset %d for script %4.4s entry %d in script list table", record.offset, (char *)&record.tag, i);
+    }
+    script_list.push_back(record);
+  }
+  if (script_list.size() != script_count) {
+    return OTS_FAILURE_MSG("Bad script list size %ld in script list table", script_list.size());
+  }
+
+  // Check script records.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!ParseScriptTable(file, data + script_list[i].offset,
+                          length - script_list[i].offset,
+                          script_list[i].tag, num_features)) {
+      return OTS_FAILURE_MSG("Failed to parse script table %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Parsing FeatureListTable requires number of lookups so we need to parse
+// LookupListTable before calling this function.
+bool ParseFeatureListTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t* num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t feature_count = 0;
+  if (!subtable.ReadU16(&feature_count)) {
+    return OTS_FAILURE_MSG("Failed to read feature count");
+  }
+
+  std::vector<FeatureRecord> feature_records;
+  feature_records.resize(feature_count);
+  const unsigned feature_record_end =
+      6 * static_cast<unsigned>(feature_count) + 2;
+  if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of feature record %d", feature_record_end);
+  }
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!subtable.ReadU32(&feature_records[i].tag) ||
+        !subtable.ReadU16(&feature_records[i].offset)) {
+      return OTS_FAILURE_MSG("Failed to read feature header %d", i);
+    }
+    // Feature record array should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > feature_records[i].tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = feature_records[i].tag;
+    if (feature_records[i].offset < feature_record_end ||
+        feature_records[i].offset >= length) {
+      return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %4.4s", feature_records[i].offset, i, (char *)&feature_records[i].tag);
+    }
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!ParseFeatureTable(file, data + feature_records[i].offset,
+                           length - feature_records[i].offset, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse feature table %d", i);
+    }
+  }
+  *num_features = feature_count;
+  return true;
+}
+
+// For parsing GPOS/GSUB tables, this function should be called at first to
+// obtain the number of lookups because parsing FeatureTableList requires
+// the number.
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t *num_lookups) {
+  Buffer subtable(data, length);
+
+  if (!subtable.ReadU16(num_lookups)) {
+    return OTS_FAILURE_MSG("Failed to read number of lookups");
+  }
+
+  std::vector<uint16_t> lookups;
+  lookups.reserve(*num_lookups);
+  const unsigned lookup_end =
+      2 * static_cast<unsigned>(*num_lookups) + 2;
+  if (lookup_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookups %d", lookup_end);
+  }
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read lookup offset %d", i);
+    }
+    if (offset < lookup_end || offset >= length) {
+      return OTS_FAILURE_MSG("Bad lookup offset %d for lookup %d", offset, i);
+    }
+    lookups.push_back(offset);
+  }
+  if (lookups.size() != *num_lookups) {
+    return OTS_FAILURE_MSG("Bad lookup offsets list size %ld", lookups.size());
+  }
+
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    if (!ParseLookupTable(file, data + lookups[i], length - lookups[i],
+                          parser)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read class defn format");
+  }
+  if (format == 1) {
+    return ParseClassDefFormat1(file, data, length, num_glyphs, num_classes);
+  } else if (format == 2) {
+    return ParseClassDefFormat2(file, data, length, num_glyphs, num_classes);
+  }
+
+  return OTS_FAILURE_MSG("Bad class defn format %d", format);
+}
+
+bool ParseCoverageTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t expected_num_glyphs) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read coverage table format");
+  }
+  if (format == 1) {
+    return ParseCoverageFormat1(file, data, length, num_glyphs, expected_num_glyphs);
+  } else if (format == 2) {
+    return ParseCoverageFormat2(file, data, length, num_glyphs, expected_num_glyphs);
+  }
+
+  return OTS_FAILURE_MSG("Bad coverage table format %d", format);
+}
+
+bool ParseDeviceTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, size_t length) {
+  Buffer subtable(data, length);
+
+  uint16_t start_size = 0;
+  uint16_t end_size = 0;
+  uint16_t delta_format = 0;
+  if (!subtable.ReadU16(&start_size) ||
+      !subtable.ReadU16(&end_size) ||
+      !subtable.ReadU16(&delta_format)) {
+    return OTS_FAILURE_MSG("Failed to read device table header");
+  }
+  if (start_size > end_size) {
+    return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size);
+  }
+  if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
+    return OTS_FAILURE_MSG("bad delta format: %u", delta_format);
+  }
+  // The number of delta values per uint16. The device table should contain
+  // at least |num_units| * 2 bytes compressed data.
+  const unsigned num_units = (end_size - start_size) /
+      (1 << (4 - delta_format)) + 1;
+  // Just skip |num_units| * 2 bytes since the compressed data could take
+  // arbitrary values.
+  if (!subtable.Skip(num_units * 2)) {
+    return OTS_FAILURE_MSG("Failed to skip data in device table");
+  }
+  return true;
+}
+
+bool ParseContextSubtable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read context subtable format");
+  }
+
+  if (format == 1) {
+    if (!ParseContextFormat1(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 1 subtable");
+    }
+  } else if (format == 2) {
+    if (!ParseContextFormat2(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 2 subtable");
+    }
+  } else if (format == 3) {
+    if (!ParseContextFormat3(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 3 subtable");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad context subtable format %d", format);
+  }
+
+  return true;
+}
+
+bool ParseChainingContextSubtable(const ots::OpenTypeFile *file,
+                                  const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read chaining context subtable format");
+  }
+
+  if (format == 1) {
+    if (!ParseChainContextFormat1(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 1 subtable");
+    }
+  } else if (format == 2) {
+    if (!ParseChainContextFormat2(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 2 subtable");
+    }
+  } else if (format == 3) {
+    if (!ParseChainContextFormat3(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 3 subtable");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad chaining context subtable format %d", format);
+  }
+
+  return true;
+}
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t lookup_type = 0;
+  uint32_t offset_extension = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU32(&offset_extension)) {
+    return OTS_FAILURE_MSG("Failed to read extension table header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad extension table format %d", format);
+  }
+  // |lookup_type| should be other than |parser->extension_type|.
+  if (lookup_type < 1 || lookup_type > parser->num_types ||
+      lookup_type == parser->extension_type) {
+    return OTS_FAILURE_MSG("Bad lookup type %d in extension table", lookup_type);
+  }
+
+  const unsigned format_end = static_cast<unsigned>(8);
+  if (offset_extension < format_end ||
+      offset_extension >= length) {
+    return OTS_FAILURE_MSG("Bad extension offset %d", offset_extension);
+  }
+
+  // Parse the extension subtable of |lookup_type|.
+  if (!parser->Parse(file, data + offset_extension, length - offset_extension,
+                     lookup_type)) {
+    return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup");
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/layout.h b/third_party/ots/src/layout.h
new file mode 100644
index 0000000..3b94589
--- /dev/null
+++ b/third_party/ots/src/layout.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LAYOUT_H_
+#define OTS_LAYOUT_H_
+
+#include "ots.h"
+
+// Utility functions for OpenType layout common table formats.
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+namespace ots {
+
+
+struct LookupSubtableParser {
+  struct TypeParser {
+    uint16_t type;
+    bool (*parse)(const OpenTypeFile *file, const uint8_t *data,
+                  const size_t length);
+  };
+  size_t num_types;
+  uint16_t extension_type;
+  const TypeParser *parsers;
+
+  bool Parse(const OpenTypeFile *file, const uint8_t *data,
+             const size_t length, const uint16_t lookup_type) const;
+};
+
+bool ParseScriptListTable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_features);
+
+bool ParseFeatureListTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t *num_features);
+
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t* num_lookups);
+
+bool ParseClassDefTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes);
+
+bool ParseCoverageTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t expected_num_glyphs = 0);
+
+bool ParseDeviceTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, size_t length);
+
+// Parser for 'Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseContextSubtable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups);
+
+// Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseChainingContextSubtable(const ots::OpenTypeFile *file,
+                                  const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups);
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser);
+
+}  // namespace ots
+
+#endif  // OTS_LAYOUT_H_
+
diff --git a/third_party/ots/src/loca.cc b/third_party/ots/src/loca.cc
new file mode 100644
index 0000000..4b291f0
--- /dev/null
+++ b/third_party/ots/src/loca.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "loca.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// loca - Index to Location
+// http://www.microsoft.com/typography/otspec/loca.htm
+
+#define TABLE_NAME "loca"
+
+namespace ots {
+
+bool ots_loca_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  // We can't do anything useful in validating this data except to ensure that
+  // the values are monotonically increasing.
+
+  OpenTypeLOCA *loca = new OpenTypeLOCA;
+  file->loca = loca;
+
+  if (!file->maxp || !file->head) {
+    return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
+  }
+
+  const unsigned num_glyphs = file->maxp->num_glyphs;
+  unsigned last_offset = 0;
+  loca->offsets.resize(num_glyphs + 1);
+  // maxp->num_glyphs is uint16_t, thus the addition never overflows.
+
+  if (file->head->index_to_loc_format == 0) {
+    // Note that the <= here (and below) is correct. There is one more offset
+    // than the number of glyphs in order to give the length of the final
+    // glyph.
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint16_t offset = 0;
+      if (!table.ReadU16(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset * 2;
+    }
+  } else {
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint32_t offset = 0;
+      if (!table.ReadU32(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset;
+    }
+  }
+
+  return true;
+}
+
+bool ots_loca_should_serialise(OpenTypeFile *file) {
+  return file->loca != NULL;
+}
+
+bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLOCA *loca = file->loca;
+  const OpenTypeHEAD *head = file->head;
+
+  if (!head) {
+    return OTS_FAILURE_MSG("Missing head table in font needed by loca");
+  }
+
+  if (head->index_to_loc_format == 0) {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
+      if ((offset != (loca->offsets[i] >> 1)) ||
+          !out->WriteU16(offset)) {
+        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+      }
+    }
+  } else {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      if (!out->WriteU32(loca->offsets[i])) {
+        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_loca_free(OpenTypeFile *file) {
+  delete file->loca;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/loca.h b/third_party/ots/src/loca.h
new file mode 100644
index 0000000..255ef06
--- /dev/null
+++ b/third_party/ots/src/loca.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LOCA_H_
+#define OTS_LOCA_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLOCA {
+  std::vector<uint32_t> offsets;
+};
+
+}  // namespace ots
+
+#endif  // OTS_LOCA_H_
diff --git a/third_party/ots/src/ltsh.cc b/third_party/ots/src/ltsh.cc
new file mode 100644
index 0000000..418c159
--- /dev/null
+++ b/third_party/ots/src/ltsh.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ltsh.h"
+
+#include "maxp.h"
+
+// LTSH - Linear Threshold
+// http://www.microsoft.com/typography/otspec/ltsh.htm
+
+#define TABLE_NAME "LTSH"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->ltsh; \
+    file->ltsh = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_ltsh_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
+  }
+
+  OpenTypeLTSH *ltsh = new OpenTypeLTSH;
+  file->ltsh = ltsh;
+
+  uint16_t num_glyphs = 0;
+  if (!table.ReadU16(&ltsh->version) ||
+      !table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read ltsh header");
+  }
+
+  if (ltsh->version != 0) {
+    DROP_THIS_TABLE("bad version: %u", ltsh->version);
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    DROP_THIS_TABLE("bad num_glyphs: %u", num_glyphs);
+    return true;
+  }
+
+  ltsh->ypels.reserve(num_glyphs);
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    uint8_t pel = 0;
+    if (!table.ReadU8(&pel)) {
+      return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i);
+    }
+    ltsh->ypels.push_back(pel);
+  }
+
+  return true;
+}
+
+bool ots_ltsh_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->ltsh != NULL;
+}
+
+bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLTSH *ltsh = file->ltsh;
+
+  const uint16_t num_ypels = static_cast<uint16_t>(ltsh->ypels.size());
+  if (num_ypels != ltsh->ypels.size() ||
+      !out->WriteU16(ltsh->version) ||
+      !out->WriteU16(num_ypels)) {
+    return OTS_FAILURE_MSG("Failed to write pels size");
+  }
+  for (uint16_t i = 0; i < num_ypels; ++i) {
+    if (!out->Write(&(ltsh->ypels[i]), 1)) {
+      return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_ltsh_free(OpenTypeFile *file) {
+  delete file->ltsh;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/ltsh.h b/third_party/ots/src/ltsh.h
new file mode 100644
index 0000000..23d97d7
--- /dev/null
+++ b/third_party/ots/src/ltsh.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LTSH_H_
+#define OTS_LTSH_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLTSH {
+  uint16_t version;
+  std::vector<uint8_t> ypels;
+};
+
+}  // namespace ots
+
+#endif  // OTS_LTSH_H_
diff --git a/third_party/ots/src/math.cc b/third_party/ots/src/math.cc
new file mode 100644
index 0000000..9124a88
--- /dev/null
+++ b/third_party/ots/src/math.cc
@@ -0,0 +1,609 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// We use an underscore to avoid confusion with the standard math.h library.
+#include "math_.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// MATH - The MATH Table
+// The specification is not yet public but has been submitted to the MPEG group
+// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
+// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
+// contact Microsoft's engineer Murray Sargent to obtain a copy.
+
+#define TABLE_NAME "MATH"
+
+namespace {
+
+// The size of MATH header.
+// Version
+// MathConstants
+// MathGlyphInfo
+// MathVariants
+const unsigned kMathHeaderSize = 4 + 3 * 2;
+
+// The size of the MathGlyphInfo header.
+// MathItalicsCorrectionInfo
+// MathTopAccentAttachment
+// ExtendedShapeCoverage
+// MathKernInfo
+const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
+
+// The size of the MathValueRecord.
+// Value
+// DeviceTable
+const unsigned kMathValueRecordSize = 2 * 2;
+
+// The size of the GlyphPartRecord.
+// glyph
+// StartConnectorLength
+// EndConnectorLength
+// FullAdvance
+// PartFlags
+const unsigned kGlyphPartRecordSize = 5 * 2;
+
+// Shared Table: MathValueRecord
+
+bool ParseMathValueRecord(const ots::OpenTypeFile *file,
+                          ots::Buffer* subtable, const uint8_t *data,
+                          const size_t length) {
+  // Check the Value field.
+  if (!subtable->Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  // Check the offset to device table.
+  uint16_t offset = 0;
+  if (!subtable->ReadU16(&offset)) {
+    return OTS_FAILURE();
+  }
+  if (offset) {
+    if (offset >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathConstantsTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, size_t length) {
+  ots::Buffer subtable(data, length);
+
+  // Part 1: int16 or uint16 constants.
+  //  ScriptPercentScaleDown
+  //  ScriptScriptPercentScaleDown
+  //  DelimitedSubFormulaMinHeight
+  //  DisplayOperatorMinHeight
+  if (!subtable.Skip(4 * 2)) {
+    return OTS_FAILURE();
+  }
+
+  // Part 2: MathValueRecord constants.
+  // MathLeading
+  // AxisHeight
+  // AccentBaseHeight
+  // FlattenedAccentBaseHeight
+  // SubscriptShiftDown
+  // SubscriptTopMax
+  // SubscriptBaselineDropMin
+  // SuperscriptShiftUp
+  // SuperscriptShiftUpCramped
+  // SuperscriptBottomMin
+  //
+  // SuperscriptBaselineDropMax
+  // SubSuperscriptGapMin
+  // SuperscriptBottomMaxWithSubscript
+  // SpaceAfterScript
+  // UpperLimitGapMin
+  // UpperLimitBaselineRiseMin
+  // LowerLimitGapMin
+  // LowerLimitBaselineDropMin
+  // StackTopShiftUp
+  // StackTopDisplayStyleShiftUp
+  //
+  // StackBottomShiftDown
+  // StackBottomDisplayStyleShiftDown
+  // StackGapMin
+  // StackDisplayStyleGapMin
+  // StretchStackTopShiftUp
+  // StretchStackBottomShiftDown
+  // StretchStackGapAboveMin
+  // StretchStackGapBelowMin
+  // FractionNumeratorShiftUp
+  // FractionNumeratorDisplayStyleShiftUp
+  //
+  // FractionDenominatorShiftDown
+  // FractionDenominatorDisplayStyleShiftDown
+  // FractionNumeratorGapMin
+  // FractionNumDisplayStyleGapMin
+  // FractionRuleThickness
+  // FractionDenominatorGapMin
+  // FractionDenomDisplayStyleGapMin
+  // SkewedFractionHorizontalGap
+  // SkewedFractionVerticalGap
+  // OverbarVerticalGap
+  //
+  // OverbarRuleThickness
+  // OverbarExtraAscender
+  // UnderbarVerticalGap
+  // UnderbarRuleThickness
+  // UnderbarExtraDescender
+  // RadicalVerticalGap
+  // RadicalDisplayStyleVerticalGap
+  // RadicalRuleThickness
+  // RadicalExtraAscender
+  // RadicalKernBeforeDegree
+  //
+  // RadicalKernAfterDegree
+  for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Part 3: uint16 constant
+  // RadicalDegreeBottomRaisePercent
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file,
+                                           ots::Buffer* subtable,
+                                           const uint8_t *data,
+                                           const size_t length,
+                                           const uint16_t num_glyphs) {
+  // Check the header.
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+  if (!subtable->ReadU16(&offset_coverage) ||
+      !subtable->ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+      sequence_count * kMathValueRecordSize;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               num_glyphs, sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence.
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    if (!ParseMathValueRecord(file, subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file,
+                                         const uint8_t *data,
+                                         size_t length,
+                                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
+                                               num_glyphs);
+}
+
+bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file,
+                                       const uint8_t *data,
+                                       size_t length,
+                                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
+                                               num_glyphs);
+}
+
+bool ParseMathKernTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length) {
+  ots::Buffer subtable(data, length);
+
+  // Check the Height count.
+  uint16_t height_count = 0;
+  if (!subtable.ReadU16(&height_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check the Correction Heights.
+  for (unsigned i = 0; i < height_count; ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check the Kern Values.
+  for (unsigned i = 0; i <= height_count; ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathKernInfoTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+    sequence_count * 4 * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage,
+                               num_glyphs, sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence of MathKernInfoRecord
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
+    for (unsigned j = 0; j < 4; ++j) {
+      uint16_t offset_math_kern = 0;
+      if (!subtable.ReadU16(&offset_math_kern)) {
+        return OTS_FAILURE();
+      }
+      if (offset_math_kern) {
+        if (offset_math_kern < sequence_end || offset_math_kern >= length ||
+            !ParseMathKernTable(file, data + offset_math_kern,
+                                length - offset_math_kern)) {
+          return OTS_FAILURE();
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, size_t length,
+                             const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check Header.
+  uint16_t offset_math_italics_correction_info = 0;
+  uint16_t offset_math_top_accent_attachment = 0;
+  uint16_t offset_extended_shaped_coverage = 0;
+  uint16_t offset_math_kern_info = 0;
+  if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
+      !subtable.ReadU16(&offset_math_top_accent_attachment) ||
+      !subtable.ReadU16(&offset_extended_shaped_coverage) ||
+      !subtable.ReadU16(&offset_math_kern_info)) {
+    return OTS_FAILURE();
+  }
+
+  // Check subtables.
+  // The specification does not say whether the offsets for
+  // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
+  // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
+  if (offset_math_italics_correction_info) {
+    if (offset_math_italics_correction_info >= length ||
+        offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
+        !ParseMathItalicsCorrectionInfoTable(
+            file, data + offset_math_italics_correction_info,
+            length - offset_math_italics_correction_info,
+            num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_math_top_accent_attachment) {
+    if (offset_math_top_accent_attachment >= length ||
+        offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
+        !ParseMathTopAccentAttachmentTable(file, data +
+                                           offset_math_top_accent_attachment,
+                                           length -
+                                           offset_math_top_accent_attachment,
+                                           num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_extended_shaped_coverage) {
+    if (offset_extended_shaped_coverage >= length ||
+        offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
+        !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage,
+                                 length - offset_extended_shaped_coverage,
+                                 num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_math_kern_info) {
+    if (offset_math_kern_info >= length ||
+        offset_math_kern_info < kMathGlyphInfoHeaderSize ||
+        !ParseMathKernInfoTable(file, data + offset_math_kern_info,
+                                length - offset_math_kern_info, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t part_count = 0;
+  if (!ParseMathValueRecord(file, &subtable, data, length) ||
+      !subtable.ReadU16(&part_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = kMathValueRecordSize +
+    static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check the sequence of GlyphPartRecord.
+  for (unsigned i = 0; i < part_count; ++i) {
+    uint16_t glyph = 0;
+    uint16_t part_flags = 0;
+    if (!subtable.ReadU16(&glyph) ||
+        !subtable.Skip(2 * 3) ||
+        !subtable.ReadU16(&part_flags)) {
+      return OTS_FAILURE();
+    }
+    if (glyph >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+    if (part_flags & ~0x00000001) {
+      return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file,
+                                     const uint8_t *data,
+                                     size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_glyph_assembly = 0;
+  uint16_t variant_count = 0;
+  if (!subtable.ReadU16(&offset_glyph_assembly) ||
+      !subtable.ReadU16(&variant_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+    variant_count * 2 * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check the GlyphAssembly offset.
+  if (offset_glyph_assembly) {
+    if (offset_glyph_assembly >= length ||
+        offset_glyph_assembly < sequence_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly,
+                                 length - offset_glyph_assembly, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check the sequence of MathGlyphVariantRecord.
+  for (unsigned i = 0; i < variant_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph) ||
+        !subtable.Skip(2)) {
+      return OTS_FAILURE();
+    }
+    if (glyph >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file,
+                                        ots::Buffer* subtable,
+                                        const uint8_t *data,
+                                        size_t length,
+                                        const uint16_t num_glyphs,
+                                        uint16_t offset_coverage,
+                                        uint16_t glyph_count,
+                                        const unsigned sequence_end) {
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               num_glyphs, glyph_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence of MathGlyphConstruction.
+  for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t offset_glyph_construction = 0;
+      if (!subtable->ReadU16(&offset_glyph_construction)) {
+        return OTS_FAILURE();
+      }
+      if (offset_glyph_construction < sequence_end ||
+          offset_glyph_construction >= length ||
+          !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction,
+                                           length - offset_glyph_construction,
+                                           num_glyphs)) {
+        return OTS_FAILURE();
+      }
+  }
+
+  return true;
+}
+
+bool ParseMathVariantsTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_vert_glyph_coverage = 0;
+  uint16_t offset_horiz_glyph_coverage = 0;
+  uint16_t vert_glyph_count = 0;
+  uint16_t horiz_glyph_count = 0;
+  if (!subtable.Skip(2) ||  // MinConnectorOverlap
+      !subtable.ReadU16(&offset_vert_glyph_coverage) ||
+      !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
+      !subtable.ReadU16(&vert_glyph_count) ||
+      !subtable.ReadU16(&horiz_glyph_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
+    horiz_glyph_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
+                                          offset_vert_glyph_coverage,
+                                          vert_glyph_count,
+                                          sequence_end) ||
+      !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
+                                          offset_horiz_glyph_coverage,
+                                          horiz_glyph_count,
+                                          sequence_end)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->math->data = 0; \
+    file->math->length = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in MATH table.
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  Buffer table(data, length);
+
+  OpenTypeMATH* math = new OpenTypeMATH;
+  file->math = math;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("bad MATH version");
+    return true;
+  }
+
+  uint16_t offset_math_constants = 0;
+  uint16_t offset_math_glyph_info = 0;
+  uint16_t offset_math_variants = 0;
+  if (!table.ReadU16(&offset_math_constants) ||
+      !table.ReadU16(&offset_math_glyph_info) ||
+      !table.ReadU16(&offset_math_variants)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_math_constants >= length ||
+      offset_math_constants < kMathHeaderSize ||
+      offset_math_glyph_info >= length ||
+      offset_math_glyph_info < kMathHeaderSize ||
+      offset_math_variants >= length ||
+      offset_math_variants < kMathHeaderSize) {
+    DROP_THIS_TABLE("bad offset in MATH header");
+    return true;
+  }
+
+  if (!ParseMathConstantsTable(file, data + offset_math_constants,
+                               length - offset_math_constants)) {
+    DROP_THIS_TABLE("failed to parse MathConstants table");
+    return true;
+  }
+  if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info,
+                               length - offset_math_glyph_info, num_glyphs)) {
+    DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
+    return true;
+  }
+  if (!ParseMathVariantsTable(file, data + offset_math_variants,
+                              length - offset_math_variants, num_glyphs)) {
+    DROP_THIS_TABLE("failed to parse MathVariants table");
+    return true;
+  }
+
+  math->data = data;
+  math->length = length;
+  return true;
+}
+
+bool ots_math_should_serialise(OpenTypeFile *file) {
+  return file->math != NULL && file->math->data != NULL;
+}
+
+bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->math->data, file->math->length)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_math_free(OpenTypeFile *file) {
+  delete file->math;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/math_.h b/third_party/ots/src/math_.h
new file mode 100644
index 0000000..91c54db
--- /dev/null
+++ b/third_party/ots/src/math_.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MATH_H_
+#define OTS_MATH_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMATH {
+  OpenTypeMATH()
+      : data(NULL),
+        length(0) {
+  }
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/maxp.cc b/third_party/ots/src/maxp.cc
new file mode 100644
index 0000000..aaf0076
--- /dev/null
+++ b/third_party/ots/src/maxp.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "maxp.h"
+
+// maxp - Maximum Profile
+// http://www.microsoft.com/typography/otspec/maxp.htm
+
+#define TABLE_NAME "maxp"
+
+namespace ots {
+
+bool ots_maxp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeMAXP *maxp = new OpenTypeMAXP;
+  file->maxp = maxp;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE_MSG("Failed to read version of maxp table");
+  }
+
+  if (version >> 16 > 1) {
+    return OTS_FAILURE_MSG("Bad maxp version %d", version);
+  }
+
+  if (!table.ReadU16(&maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
+  }
+
+  if (!maxp->num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
+  }
+
+  if (version >> 16 == 1) {
+    maxp->version_1 = true;
+    if (!table.ReadU16(&maxp->max_points) ||
+        !table.ReadU16(&maxp->max_contours) ||
+        !table.ReadU16(&maxp->max_c_points) ||
+        !table.ReadU16(&maxp->max_c_contours) ||
+        !table.ReadU16(&maxp->max_zones) ||
+        !table.ReadU16(&maxp->max_t_points) ||
+        !table.ReadU16(&maxp->max_storage) ||
+        !table.ReadU16(&maxp->max_fdefs) ||
+        !table.ReadU16(&maxp->max_idefs) ||
+        !table.ReadU16(&maxp->max_stack) ||
+        !table.ReadU16(&maxp->max_size_glyf_instructions) ||
+        !table.ReadU16(&maxp->max_c_components) ||
+        !table.ReadU16(&maxp->max_c_depth)) {
+      return OTS_FAILURE_MSG("Failed to read maxp table");
+    }
+
+    if (maxp->max_zones == 0) {
+      // workaround for ipa*.ttf Japanese fonts.
+      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+      maxp->max_zones = 1;
+    } else if (maxp->max_zones == 3) {
+      // workaround for Ecolier-*.ttf fonts.
+      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+      maxp->max_zones = 2;
+    }
+
+    if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
+      return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
+    }
+  } else {
+    maxp->version_1 = false;
+  }
+
+  return true;
+}
+
+bool ots_maxp_should_serialise(OpenTypeFile *file) {
+  return file->maxp != NULL;
+}
+
+bool ots_maxp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeMAXP *maxp = file->maxp;
+
+  if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
+      !out->WriteU16(maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
+  }
+
+  if (!maxp->version_1) return true;
+
+  if (!out->WriteU16(maxp->max_points) ||
+      !out->WriteU16(maxp->max_contours) ||
+      !out->WriteU16(maxp->max_c_points) ||
+      !out->WriteU16(maxp->max_c_contours)) {
+    return OTS_FAILURE_MSG("Failed to write maxp");
+  }
+
+  if (!out->WriteU16(maxp->max_zones) ||
+      !out->WriteU16(maxp->max_t_points) ||
+      !out->WriteU16(maxp->max_storage) ||
+      !out->WriteU16(maxp->max_fdefs) ||
+      !out->WriteU16(maxp->max_idefs) ||
+      !out->WriteU16(maxp->max_stack) ||
+      !out->WriteU16(maxp->max_size_glyf_instructions)) {
+    return OTS_FAILURE_MSG("Failed to write more maxp");
+  }
+
+  if (!out->WriteU16(maxp->max_c_components) ||
+      !out->WriteU16(maxp->max_c_depth)) {
+    return OTS_FAILURE_MSG("Failed to write yet more maxp");
+  }
+
+  return true;
+}
+
+void ots_maxp_free(OpenTypeFile *file) {
+  delete file->maxp;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/maxp.h b/third_party/ots/src/maxp.h
new file mode 100644
index 0000000..efca0c9
--- /dev/null
+++ b/third_party/ots/src/maxp.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MAXP_H_
+#define OTS_MAXP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMAXP {
+  uint16_t num_glyphs;
+  bool version_1;
+
+  uint16_t max_points;
+  uint16_t max_contours;
+  uint16_t max_c_points;
+  uint16_t max_c_contours;
+
+  uint16_t max_zones;
+  uint16_t max_t_points;
+  uint16_t max_storage;
+  uint16_t max_fdefs;
+  uint16_t max_idefs;
+  uint16_t max_stack;
+  uint16_t max_size_glyf_instructions;
+
+  uint16_t max_c_components;
+  uint16_t max_c_depth;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MAXP_H_
diff --git a/third_party/ots/src/metrics.cc b/third_party/ots/src/metrics.cc
new file mode 100644
index 0000000..8d59b95
--- /dev/null
+++ b/third_party/ots/src/metrics.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// OpenType horizontal and vertical common header format
+// http://www.microsoft.com/typography/otspec/hhea.htm
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "metrics" // XXX: use individual table names
+
+namespace ots {
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header) {
+  if (!table->ReadS16(&header->ascent) ||
+      !table->ReadS16(&header->descent) ||
+      !table->ReadS16(&header->linegap) ||
+      !table->ReadU16(&header->adv_width_max) ||
+      !table->ReadS16(&header->min_sb1) ||
+      !table->ReadS16(&header->min_sb2) ||
+      !table->ReadS16(&header->max_extent) ||
+      !table->ReadS16(&header->caret_slope_rise) ||
+      !table->ReadS16(&header->caret_slope_run) ||
+      !table->ReadS16(&header->caret_offset)) {
+    return OTS_FAILURE_MSG("Failed to read metrics header");
+  }
+
+  if (header->ascent < 0) {
+    OTS_WARNING("bad ascent: %d", header->ascent);
+    header->ascent = 0;
+  }
+  if (header->linegap < 0) {
+    OTS_WARNING("bad linegap: %d", header->linegap);
+    header->linegap = 0;
+  }
+
+  if (!file->head) {
+    return OTS_FAILURE_MSG("Missing head font table");
+  }
+
+  // if the font is non-slanted, caret_offset should be zero.
+  if (!(file->head->mac_style & 2) &&
+      (header->caret_offset != 0)) {
+    OTS_WARNING("bad caret offset: %d", header->caret_offset);
+    header->caret_offset = 0;
+  }
+
+  // skip the reserved bytes
+  if (!table->Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
+  }
+
+  int16_t data_format;
+  if (!table->ReadS16(&data_format)) {
+    return OTS_FAILURE_MSG("Failed to read data format");
+  }
+  if (data_format) {
+    return OTS_FAILURE_MSG("Bad data format %d", data_format);
+  }
+
+  if (!table->ReadU16(&header->num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to read number of metrics");
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp font table");
+  }
+
+  if (header->num_metrics > file->maxp->num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsHeader(const ots::OpenTypeFile *file,
+                            OTSStream *out,
+                            const OpenTypeMetricsHeader *header) {
+  if (!out->WriteU32(header->version) ||
+      !out->WriteS16(header->ascent) ||
+      !out->WriteS16(header->descent) ||
+      !out->WriteS16(header->linegap) ||
+      !out->WriteU16(header->adv_width_max) ||
+      !out->WriteS16(header->min_sb1) ||
+      !out->WriteS16(header->min_sb2) ||
+      !out->WriteS16(header->max_extent) ||
+      !out->WriteS16(header->caret_slope_rise) ||
+      !out->WriteS16(header->caret_slope_run) ||
+      !out->WriteS16(header->caret_offset) ||
+      !out->WriteR64(0) ||  // reserved
+      !out->WriteS16(0) ||  // metric data format
+      !out->WriteU16(header->num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to write metrics");
+  }
+
+  return true;
+}
+
+bool ParseMetricsTable(const ots::OpenTypeFile *file,
+                       Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics) {
+  // |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
+  // amount of memory that we'll allocate for this to a sane amount.
+  const unsigned num_metrics = header->num_metrics;
+
+  if (num_metrics > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics);
+  }
+  if (!num_metrics) {
+    return OTS_FAILURE_MSG("No metrics!");
+  }
+  const unsigned num_sbs = num_glyphs - num_metrics;
+
+  metrics->entries.reserve(num_metrics);
+  for (unsigned i = 0; i < num_metrics; ++i) {
+    uint16_t adv = 0;
+    int16_t sb = 0;
+    if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
+      return OTS_FAILURE_MSG("Failed to read metric %d", i);
+    }
+
+    // This check is bogus, see https://github.com/khaledhosny/ots/issues/36
+#if 0
+    // Since so many fonts don't have proper value on |adv| and |sb|,
+    // we should not call ots_failure() here. For example, about 20% of fonts
+    // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
+    if (adv > header->adv_width_max) {
+      OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max);
+      adv = header->adv_width_max;
+    }
+
+    if (sb < header->min_sb1) {
+      OTS_WARNING("bad sb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+#endif
+
+    metrics->entries.push_back(std::make_pair(adv, sb));
+  }
+
+  metrics->sbs.reserve(num_sbs);
+  for (unsigned i = 0; i < num_sbs; ++i) {
+    int16_t sb;
+    if (!table->ReadS16(&sb)) {
+      // Some Japanese fonts (e.g., mona.ttf) fail this test.
+      return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics);
+    }
+
+    // This check is bogus, see https://github.com/khaledhosny/ots/issues/36
+#if 0
+    if (sb < header->min_sb1) {
+      // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
+      // (e.g., Notice2Std.otf) have weird lsb values.
+      OTS_WARNING("bad lsb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+#endif
+
+    metrics->sbs.push_back(sb);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsTable(const ots::OpenTypeFile *file,
+                           OTSStream *out,
+                           const OpenTypeMetricsTable *metrics) {
+  for (unsigned i = 0; i < metrics->entries.size(); ++i) {
+    if (!out->WriteU16(metrics->entries[i].first) ||
+        !out->WriteS16(metrics->entries[i].second)) {
+      return OTS_FAILURE_MSG("Failed to write metric %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
+    if (!out->WriteS16(metrics->sbs[i])) {
+      return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size());
+    }
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/metrics.h b/third_party/ots/src/metrics.h
new file mode 100644
index 0000000..f0b4ee8
--- /dev/null
+++ b/third_party/ots/src/metrics.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_METRICS_H_
+#define OTS_METRICS_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMetricsHeader {
+  uint32_t version;
+  int16_t ascent;
+  int16_t descent;
+  int16_t linegap;
+  uint16_t adv_width_max;
+  int16_t min_sb1;
+  int16_t min_sb2;
+  int16_t max_extent;
+  int16_t caret_slope_rise;
+  int16_t caret_slope_run;
+  int16_t caret_offset;
+  uint16_t num_metrics;
+};
+
+struct OpenTypeMetricsTable {
+  std::vector<std::pair<uint16_t, int16_t> > entries;
+  std::vector<int16_t> sbs;
+};
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header);
+bool SerialiseMetricsHeader(const ots::OpenTypeFile *file,
+                            OTSStream *out,
+                            const OpenTypeMetricsHeader *header);
+
+bool ParseMetricsTable(const ots::OpenTypeFile *file,
+                       Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics);
+bool SerialiseMetricsTable(const ots::OpenTypeFile *file,
+                           OTSStream *out,
+                           const OpenTypeMetricsTable *metrics);
+
+}  // namespace ots
+
+#endif  // OTS_METRICS_H_
+
diff --git a/third_party/ots/src/name.cc b/third_party/ots/src/name.cc
new file mode 100644
index 0000000..2ea10dc
--- /dev/null
+++ b/third_party/ots/src/name.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "name.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "cff.h"
+
+// name - Naming Table
+// http://www.microsoft.com/typography/otspec/name.htm
+
+#define TABLE_NAME "name"
+
+namespace {
+
+bool ValidInPsName(char c) {
+  return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
+}
+
+bool CheckPsNameAscii(const std::string& name) {
+  for (unsigned i = 0; i < name.size(); ++i) {
+    if (!ValidInPsName(name[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CheckPsNameUtf16Be(const std::string& name) {
+  if ((name.size() & 1) != 0)
+    return false;
+
+  for (unsigned i = 0; i < name.size(); i += 2) {
+    if (name[i] != 0) {
+      return false;
+    }
+    if (!ValidInPsName(name[i+1])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void AssignToUtf16BeFromAscii(std::string* target,
+                              const std::string& source) {
+  target->resize(source.size() * 2);
+  for (unsigned i = 0, j = 0; i < source.size(); i++) {
+    (*target)[j++] = '\0';
+    (*target)[j++] = source[i];
+  }
+}
+
+}  // namespace
+
+
+namespace ots {
+
+bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeNAME* name = new OpenTypeNAME;
+  file->name = name;
+
+  uint16_t format = 0;
+  if (!table.ReadU16(&format) || format > 1) {
+    return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
+  }
+
+  uint16_t count = 0;
+  if (!table.ReadU16(&count)) {
+    return OTS_FAILURE_MSG("Failed to read name count");
+  }
+
+  uint16_t string_offset = 0;
+  if (!table.ReadU16(&string_offset) || string_offset > length) {
+    return OTS_FAILURE_MSG("Failed to read strings offset");
+  }
+  const char* string_base = reinterpret_cast<const char*>(data) +
+      string_offset;
+
+  NameRecord prev_record;
+  bool sort_required = false;
+
+  // Read all the names, discarding any with invalid IDs,
+  // and any where the offset/length would be outside the table.
+  // A stricter alternative would be to reject the font if there
+  // are invalid name records, but it's not clear that is necessary.
+  for (unsigned i = 0; i < count; ++i) {
+    NameRecord rec;
+    uint16_t name_length, name_offset = 0;
+    if (!table.ReadU16(&rec.platform_id) ||
+        !table.ReadU16(&rec.encoding_id) ||
+        !table.ReadU16(&rec.language_id) ||
+        !table.ReadU16(&rec.name_id) ||
+        !table.ReadU16(&name_length) ||
+        !table.ReadU16(&name_offset)) {
+      return OTS_FAILURE_MSG("Failed to read name entry %d", i);
+    }
+    // check platform & encoding, discard names with unknown values
+    switch (rec.platform_id) {
+      case 0:  // Unicode
+        if (rec.encoding_id > 6) {
+          continue;
+        }
+        break;
+      case 1:  // Macintosh
+        if (rec.encoding_id > 32) {
+          continue;
+        }
+        break;
+      case 2:  // ISO
+        if (rec.encoding_id > 2) {
+          continue;
+        }
+        break;
+      case 3:  // Windows: IDs 7 to 9 are "reserved"
+        if (rec.encoding_id > 6 && rec.encoding_id != 10) {
+          continue;
+        }
+        break;
+      case 4:  // Custom (OTF Windows NT compatibility)
+        if (rec.encoding_id > 255) {
+          continue;
+        }
+        break;
+      default:  // unknown platform
+        continue;
+    }
+
+    const unsigned name_end = static_cast<unsigned>(string_offset) +
+        name_offset + name_length;
+    if (name_end > length) {
+      continue;
+    }
+    rec.text.resize(name_length);
+    rec.text.assign(string_base + name_offset, name_length);
+
+    if (rec.name_id == 6) {
+      // PostScript name: check that it is valid, if not then discard it
+      if (rec.platform_id == 1) {
+        if (file->cff && !file->cff->name.empty()) {
+          rec.text = file->cff->name;
+        } else if (!CheckPsNameAscii(rec.text)) {
+          continue;
+        }
+      } else if (rec.platform_id == 0 || rec.platform_id == 3) {
+        if (file->cff && !file->cff->name.empty()) {
+          AssignToUtf16BeFromAscii(&rec.text, file->cff->name);
+        } else if (!CheckPsNameUtf16Be(rec.text)) {
+          continue;
+        }
+      }
+    }
+
+    if ((i > 0) && !(prev_record < rec)) {
+      OTS_WARNING("name records are not sorted.");
+      sort_required = true;
+    }
+
+    name->names.push_back(rec);
+    prev_record = rec;
+  }
+
+  if (format == 1) {
+    // extended name table format with language tags
+    uint16_t lang_tag_count;
+    if (!table.ReadU16(&lang_tag_count)) {
+      return OTS_FAILURE_MSG("Failed to read language tag count");
+    }
+    for (unsigned i = 0; i < lang_tag_count; ++i) {
+      uint16_t tag_length = 0;
+      uint16_t tag_offset = 0;
+      if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
+        return OTS_FAILURE_MSG("Faile to read tag length or offset");
+      }
+      const unsigned tag_end = static_cast<unsigned>(string_offset) +
+          tag_offset + tag_length;
+      if (tag_end > length) {
+        return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
+      }
+      std::string tag(string_base + tag_offset, tag_length);
+      name->lang_tags.push_back(tag);
+    }
+  }
+
+  if (table.offset() > string_offset) {
+    // the string storage apparently overlapped the name/tag records;
+    // consider this font to be badly broken
+    return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
+  }
+
+  // check existence of required name strings (synthesize if necessary)
+  //  [0 - copyright - skip]
+  //   1 - family
+  //   2 - subfamily
+  //  [3 - unique ID - skip]
+  //   4 - full name
+  //   5 - version
+  //   6 - postscript name
+  static const uint16_t kStdNameCount = 7;
+  static const char* kStdNames[kStdNameCount] = {
+    NULL,
+    "OTS derived font",
+    "Unspecified",
+    NULL,
+    "OTS derived font",
+    "1.000",
+    "OTS-derived-font"
+  };
+  // The spec says that "In CFF OpenType fonts, these two name strings, when
+  // translated to ASCII, must also be identical to the font name as stored in
+  // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that.
+  if (file->cff && !file->cff->name.empty()) {
+    kStdNames[6] = file->cff->name.c_str();
+  }
+
+  // scan the names to check whether the required "standard" ones are present;
+  // if not, we'll add our fixed versions here
+  bool mac_name[kStdNameCount] = { 0 };
+  bool win_name[kStdNameCount] = { 0 };
+  for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
+       name_iter != name->names.end(); name_iter++) {
+    const uint16_t id = name_iter->name_id;
+    if (id >= kStdNameCount || kStdNames[id] == NULL) {
+      continue;
+    }
+    if (name_iter->platform_id == 1) {
+      mac_name[id] = true;
+      continue;
+    }
+    if (name_iter->platform_id == 3) {
+      win_name[id] = true;
+      continue;
+    }
+  }
+
+  for (uint16_t i = 0; i < kStdNameCount; ++i) {
+    if (kStdNames[i] == NULL) {
+      continue;
+    }
+    if (!mac_name[i]) {
+      NameRecord rec(1 /* platform_id */, 0 /* encoding_id */,
+                     0 /* language_id */ , i /* name_id */);
+      rec.text.assign(kStdNames[i]);
+      name->names.push_back(rec);
+      sort_required = true;
+    }
+    if (!win_name[i]) {
+      NameRecord rec(3 /* platform_id */, 1 /* encoding_id */,
+                     1033 /* language_id */ , i /* name_id */);
+      AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i]));
+      name->names.push_back(rec);
+      sort_required = true;
+    }
+  }
+
+  if (sort_required) {
+    std::sort(name->names.begin(), name->names.end());
+  }
+
+  return true;
+}
+
+bool ots_name_should_serialise(OpenTypeFile* file) {
+  return file->name != NULL;
+}
+
+bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) {
+  const OpenTypeNAME* name = file->name;
+
+  uint16_t name_count = static_cast<uint16_t>(name->names.size());
+  uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
+  uint16_t format = 0;
+  size_t string_offset = 6 + name_count * 12;
+
+  if (name->lang_tags.size() > 0) {
+    // lang tags require a format-1 name table
+    format = 1;
+    string_offset += 2 + lang_tag_count * 4;
+  }
+  if (string_offset > 0xffff) {
+    return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
+  }
+  if (!out->WriteU16(format) ||
+      !out->WriteU16(name_count) ||
+      !out->WriteU16(static_cast<uint16_t>(string_offset))) {
+    return OTS_FAILURE_MSG("Failed to write name header");
+  }
+
+  std::string string_data;
+  for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
+       name_iter != name->names.end(); name_iter++) {
+    const NameRecord& rec = *name_iter;
+    if (string_data.size() + rec.text.size() >
+            std::numeric_limits<uint16_t>::max() ||
+        !out->WriteU16(rec.platform_id) ||
+        !out->WriteU16(rec.encoding_id) ||
+        !out->WriteU16(rec.language_id) ||
+        !out->WriteU16(rec.name_id) ||
+        !out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
+        !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
+      return OTS_FAILURE_MSG("Faile to write name entry");
+    }
+    string_data.append(rec.text);
+  }
+
+  if (format == 1) {
+    if (!out->WriteU16(lang_tag_count)) {
+      return OTS_FAILURE_MSG("Faile to write language tag count");
+    }
+    for (std::vector<std::string>::const_iterator tag_iter =
+             name->lang_tags.begin();
+         tag_iter != name->lang_tags.end(); tag_iter++) {
+      if (string_data.size() + tag_iter->size() >
+              std::numeric_limits<uint16_t>::max() ||
+          !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
+          !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
+        return OTS_FAILURE_MSG("Failed to write string");
+      }
+      string_data.append(*tag_iter);
+    }
+  }
+
+  if (!out->Write(string_data.data(), string_data.size())) {
+    return OTS_FAILURE_MSG("Faile to write string data");
+  }
+
+  return true;
+}
+
+void ots_name_free(OpenTypeFile* file) {
+  delete file->name;
+}
+
+}  // namespace
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/name.h b/third_party/ots/src/name.h
new file mode 100644
index 0000000..a11965f
--- /dev/null
+++ b/third_party/ots/src/name.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_NAME_H_
+#define OTS_NAME_H_
+
+#include <new>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct NameRecord {
+  NameRecord() {
+  }
+
+  NameRecord(uint16_t platformID, uint16_t encodingID,
+             uint16_t languageID, uint16_t nameID)
+    : platform_id(platformID),
+      encoding_id(encodingID),
+      language_id(languageID),
+      name_id(nameID) {
+  }
+
+  uint16_t platform_id;
+  uint16_t encoding_id;
+  uint16_t language_id;
+  uint16_t name_id;
+  std::string text;
+
+  bool operator<(const NameRecord& rhs) const {
+    if (platform_id < rhs.platform_id) return true;
+    if (platform_id > rhs.platform_id) return false;
+    if (encoding_id < rhs.encoding_id) return true;
+    if (encoding_id > rhs.encoding_id) return false;
+    if (language_id < rhs.language_id) return true;
+    if (language_id > rhs.language_id) return false;
+    return name_id < rhs.name_id;
+  }
+};
+
+struct OpenTypeNAME {
+  std::vector<NameRecord> names;
+  std::vector<std::string> lang_tags;
+};
+
+}  // namespace ots
+
+#endif  // OTS_NAME_H_
diff --git a/third_party/ots/src/os2.cc b/third_party/ots/src/os2.cc
new file mode 100644
index 0000000..915877e
--- /dev/null
+++ b/third_party/ots/src/os2.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "os2.h"
+
+#include "head.h"
+
+// OS/2 - OS/2 and Windows Metrics
+// http://www.microsoft.com/typography/otspec/os2.htm
+
+#define TABLE_NAME "OS/2"
+
+namespace ots {
+
+bool ots_os2_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeOS2 *os2 = new OpenTypeOS2;
+  file->os2 = os2;
+
+  if (!table.ReadU16(&os2->version) ||
+      !table.ReadS16(&os2->avg_char_width) ||
+      !table.ReadU16(&os2->weight_class) ||
+      !table.ReadU16(&os2->width_class) ||
+      !table.ReadU16(&os2->type) ||
+      !table.ReadS16(&os2->subscript_x_size) ||
+      !table.ReadS16(&os2->subscript_y_size) ||
+      !table.ReadS16(&os2->subscript_x_offset) ||
+      !table.ReadS16(&os2->subscript_y_offset) ||
+      !table.ReadS16(&os2->superscript_x_size) ||
+      !table.ReadS16(&os2->superscript_y_size) ||
+      !table.ReadS16(&os2->superscript_x_offset) ||
+      !table.ReadS16(&os2->superscript_y_offset) ||
+      !table.ReadS16(&os2->strikeout_size) ||
+      !table.ReadS16(&os2->strikeout_position) ||
+      !table.ReadS16(&os2->family_class)) {
+    return OTS_FAILURE_MSG("Failed toi read basic os2 elements");
+  }
+
+  if (os2->version > 4) {
+    return OTS_FAILURE_MSG("os2 version too high %d", os2->version);
+  }
+
+  // Some linux fonts (e.g., Kedage-t.ttf and LucidaSansDemiOblique.ttf) have
+  // weird weight/width classes. Overwrite them with FW_NORMAL/1/9.
+  if (os2->weight_class < 100 ||
+      os2->weight_class > 900 ||
+      os2->weight_class % 100) {
+    OTS_WARNING("bad weight: %u", os2->weight_class);
+    os2->weight_class = 400;  // FW_NORMAL
+  }
+  if (os2->width_class < 1) {
+    OTS_WARNING("bad width: %u", os2->width_class);
+    os2->width_class = 1;
+  } else if (os2->width_class > 9) {
+    OTS_WARNING("bad width: %u", os2->width_class);
+    os2->width_class = 9;
+  }
+
+  // lowest 3 bits of fsType are exclusive.
+  if (os2->type & 0x2) {
+    // mask bits 2 & 3.
+    os2->type &= 0xfff3u;
+  } else if (os2->type & 0x4) {
+    // mask bits 1 & 3.
+    os2->type &= 0xfff4u;
+  } else if (os2->type & 0x8) {
+    // mask bits 1 & 2.
+    os2->type &= 0xfff9u;
+  }
+
+  // mask reserved bits. use only 0..3, 8, 9 bits.
+  os2->type &= 0x30f;
+
+  if (os2->subscript_x_size < 0) {
+    OTS_WARNING("bad subscript_x_size: %d", os2->subscript_x_size);
+    os2->subscript_x_size = 0;
+  }
+  if (os2->subscript_y_size < 0) {
+    OTS_WARNING("bad subscript_y_size: %d", os2->subscript_y_size);
+    os2->subscript_y_size = 0;
+  }
+  if (os2->superscript_x_size < 0) {
+    OTS_WARNING("bad superscript_x_size: %d", os2->superscript_x_size);
+    os2->superscript_x_size = 0;
+  }
+  if (os2->superscript_y_size < 0) {
+    OTS_WARNING("bad superscript_y_size: %d", os2->superscript_y_size);
+    os2->superscript_y_size = 0;
+  }
+  if (os2->strikeout_size < 0) {
+    OTS_WARNING("bad strikeout_size: %d", os2->strikeout_size);
+    os2->strikeout_size = 0;
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!table.ReadU8(&os2->panose[i])) {
+      return OTS_FAILURE_MSG("Failed to read panose in os2 table");
+    }
+  }
+
+  if (!table.ReadU32(&os2->unicode_range_1) ||
+      !table.ReadU32(&os2->unicode_range_2) ||
+      !table.ReadU32(&os2->unicode_range_3) ||
+      !table.ReadU32(&os2->unicode_range_4) ||
+      !table.ReadU32(&os2->vendor_id) ||
+      !table.ReadU16(&os2->selection) ||
+      !table.ReadU16(&os2->first_char_index) ||
+      !table.ReadU16(&os2->last_char_index) ||
+      !table.ReadS16(&os2->typo_ascender) ||
+      !table.ReadS16(&os2->typo_descender) ||
+      !table.ReadS16(&os2->typo_linegap) ||
+      !table.ReadU16(&os2->win_ascent) ||
+      !table.ReadU16(&os2->win_descent)) {
+    return OTS_FAILURE_MSG("Failed to read more basic os2 fields");
+  }
+
+  // If bit 6 is set, then bits 0 and 5 must be clear.
+  if (os2->selection & 0x40) {
+    os2->selection &= 0xffdeu;
+  }
+
+  // the settings of bits 0 and 1 must be reflected in the macStyle bits
+  // in the 'head' table.
+  if (!file->head) {
+    return OTS_FAILURE_MSG("Head table missing from font as needed by os2 table");
+  }
+  if ((os2->selection & 0x1) &&
+      !(file->head->mac_style & 0x2)) {
+    OTS_WARNING("adjusting Mac style (italic)");
+    file->head->mac_style |= 0x2;
+  }
+  if ((os2->selection & 0x2) &&
+      !(file->head->mac_style & 0x4)) {
+    OTS_WARNING("adjusting Mac style (underscore)");
+    file->head->mac_style |= 0x4;
+  }
+
+  // While bit 6 on implies that bits 0 and 1 of macStyle are clear,
+  // the reverse is not true.
+  if ((os2->selection & 0x40) &&
+      (file->head->mac_style & 0x3)) {
+    OTS_WARNING("adjusting Mac style (regular)");
+    file->head->mac_style &= 0xfffcu;
+  }
+
+  if ((os2->version < 4) &&
+      (os2->selection & 0x300)) {
+    // bit 8 and 9 must be unset in OS/2 table versions less than 4.
+    return OTS_FAILURE_MSG("OS2 version %d incompatible with selection %d", os2->version, os2->selection);
+  }
+
+  // mask reserved bits. use only 0..9 bits.
+  os2->selection &= 0x3ff;
+
+  if (os2->first_char_index > os2->last_char_index) {
+    return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index);
+  }
+  if (os2->typo_linegap < 0) {
+    OTS_WARNING("bad linegap: %d", os2->typo_linegap);
+    os2->typo_linegap = 0;
+  }
+
+  if (os2->version < 1) {
+    // http://www.microsoft.com/typography/otspec/os2ver0.htm
+    return true;
+  }
+
+  if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
+    OTS_WARNING("bad version number: %u", os2->version);
+    // Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
+    // numbers. Fix them.
+    os2->version = 0;
+    return true;
+  }
+
+  if (!table.ReadU32(&os2->code_page_range_1) ||
+      !table.ReadU32(&os2->code_page_range_2)) {
+    return OTS_FAILURE_MSG("Failed to read codepage ranges");
+  }
+
+  if (os2->version < 2) {
+    // http://www.microsoft.com/typography/otspec/os2ver1.htm
+    return true;
+  }
+
+  if (length < offsetof(OpenTypeOS2, max_context)) {
+    OTS_WARNING("bad version number: %u", os2->version);
+    // some Japanese fonts (e.g., mona.ttf) have weird version number.
+    // fix them.
+    os2->version = 1;
+    return true;
+  }
+
+  if (!table.ReadS16(&os2->x_height) ||
+      !table.ReadS16(&os2->cap_height) ||
+      !table.ReadU16(&os2->default_char) ||
+      !table.ReadU16(&os2->break_char) ||
+      !table.ReadU16(&os2->max_context)) {
+    return OTS_FAILURE_MSG("Failed to read os2 version 2 information");
+  }
+
+  if (os2->x_height < 0) {
+    OTS_WARNING("bad x_height: %d", os2->x_height);
+    os2->x_height = 0;
+  }
+  if (os2->cap_height < 0) {
+    OTS_WARNING("bad cap_height: %d", os2->cap_height);
+    os2->cap_height = 0;
+  }
+
+  return true;
+}
+
+bool ots_os2_should_serialise(OpenTypeFile *file) {
+  return file->os2 != NULL;
+}
+
+bool ots_os2_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeOS2 *os2 = file->os2;
+
+  if (!out->WriteU16(os2->version) ||
+      !out->WriteS16(os2->avg_char_width) ||
+      !out->WriteU16(os2->weight_class) ||
+      !out->WriteU16(os2->width_class) ||
+      !out->WriteU16(os2->type) ||
+      !out->WriteS16(os2->subscript_x_size) ||
+      !out->WriteS16(os2->subscript_y_size) ||
+      !out->WriteS16(os2->subscript_x_offset) ||
+      !out->WriteS16(os2->subscript_y_offset) ||
+      !out->WriteS16(os2->superscript_x_size) ||
+      !out->WriteS16(os2->superscript_y_size) ||
+      !out->WriteS16(os2->superscript_x_offset) ||
+      !out->WriteS16(os2->superscript_y_offset) ||
+      !out->WriteS16(os2->strikeout_size) ||
+      !out->WriteS16(os2->strikeout_position) ||
+      !out->WriteS16(os2->family_class)) {
+    return OTS_FAILURE_MSG("Failed to write basic OS2 information");
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!out->Write(&os2->panose[i], 1)) {
+      return OTS_FAILURE_MSG("Failed to write os2 panose information");
+    }
+  }
+
+  if (!out->WriteU32(os2->unicode_range_1) ||
+      !out->WriteU32(os2->unicode_range_2) ||
+      !out->WriteU32(os2->unicode_range_3) ||
+      !out->WriteU32(os2->unicode_range_4) ||
+      !out->WriteU32(os2->vendor_id) ||
+      !out->WriteU16(os2->selection) ||
+      !out->WriteU16(os2->first_char_index) ||
+      !out->WriteU16(os2->last_char_index) ||
+      !out->WriteS16(os2->typo_ascender) ||
+      !out->WriteS16(os2->typo_descender) ||
+      !out->WriteS16(os2->typo_linegap) ||
+      !out->WriteU16(os2->win_ascent) ||
+      !out->WriteU16(os2->win_descent)) {
+    return OTS_FAILURE_MSG("Failed to write os2 version 1 information");
+  }
+
+  if (os2->version < 1) {
+    return true;
+  }
+
+  if (!out->WriteU32(os2->code_page_range_1) ||
+      !out->WriteU32(os2->code_page_range_2)) {
+    return OTS_FAILURE_MSG("Failed to write codepage ranges");
+  }
+
+  if (os2->version < 2) {
+    return true;
+  }
+
+  if (!out->WriteS16(os2->x_height) ||
+      !out->WriteS16(os2->cap_height) ||
+      !out->WriteU16(os2->default_char) ||
+      !out->WriteU16(os2->break_char) ||
+      !out->WriteU16(os2->max_context)) {
+    return OTS_FAILURE_MSG("Failed to write os2 version 2 information");
+  }
+
+  return true;
+}
+
+void ots_os2_free(OpenTypeFile *file) {
+  delete file->os2;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/os2.h b/third_party/ots/src/os2.h
new file mode 100644
index 0000000..9e0fc34
--- /dev/null
+++ b/third_party/ots/src/os2.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_OS2_H_
+#define OTS_OS2_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeOS2 {
+  uint16_t version;
+  int16_t avg_char_width;
+  uint16_t weight_class;
+  uint16_t width_class;
+  uint16_t type;
+  int16_t subscript_x_size;
+  int16_t subscript_y_size;
+  int16_t subscript_x_offset;
+  int16_t subscript_y_offset;
+  int16_t superscript_x_size;
+  int16_t superscript_y_size;
+  int16_t superscript_x_offset;
+  int16_t superscript_y_offset;
+  int16_t strikeout_size;
+  int16_t strikeout_position;
+  int16_t family_class;
+  uint8_t panose[10];
+  uint32_t unicode_range_1;
+  uint32_t unicode_range_2;
+  uint32_t unicode_range_3;
+  uint32_t unicode_range_4;
+  uint32_t vendor_id;
+  uint16_t selection;
+  uint16_t first_char_index;
+  uint16_t last_char_index;
+  int16_t typo_ascender;
+  int16_t typo_descender;
+  int16_t typo_linegap;
+  uint16_t win_ascent;
+  uint16_t win_descent;
+  uint32_t code_page_range_1;
+  uint32_t code_page_range_2;
+  int16_t x_height;
+  int16_t cap_height;
+  uint16_t default_char;
+  uint16_t break_char;
+  uint16_t max_context;
+};
+
+}  // namespace ots
+
+#endif  // OTS_OS2_H_
diff --git a/third_party/ots/src/ots.cc b/third_party/ots/src/ots.cc
new file mode 100644
index 0000000..197c649
--- /dev/null
+++ b/third_party/ots/src/ots.cc
@@ -0,0 +1,824 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ots.h"
+
+#include <sys/types.h>
+#include <zlib.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <vector>
+
+#include "woff2.h"
+
+// The OpenType Font File
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+namespace {
+
+// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
+#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
+#define OTS_FAILURE_MSG_HDR(msg_)      OTS_FAILURE_MSG_(header, msg_)
+
+
+struct OpenTypeTable {
+  uint32_t tag;
+  uint32_t chksum;
+  uint32_t offset;
+  uint32_t length;
+  uint32_t uncompressed_length;
+};
+
+bool CheckTag(uint32_t tag_value) {
+  for (unsigned i = 0; i < 4; ++i) {
+    const uint32_t check = tag_value & 0xff;
+    if (check < 32 || check > 126) {
+      return false;  // non-ASCII character found.
+    }
+    tag_value >>= 8;
+  }
+  return true;
+}
+
+uint32_t Tag(const char *tag_str) {	
+  uint32_t ret;	
+  std::memcpy(&ret, tag_str, 4);	
+  return ret;	
+}
+
+struct OutputTable {
+  uint32_t tag;
+  size_t offset;
+  size_t length;
+  uint32_t chksum;
+
+  static bool SortByTag(const OutputTable& a, const OutputTable& b) {
+    const uint32_t atag = ntohl(a.tag);
+    const uint32_t btag = ntohl(b.tag);
+    return atag < btag;
+  }
+};
+
+struct Arena {
+ public:
+  ~Arena() {
+    for (std::vector<uint8_t*>::iterator
+         i = hunks_.begin(); i != hunks_.end(); ++i) {
+      delete[] *i;
+    }
+  }
+
+  uint8_t* Allocate(size_t length) {
+    uint8_t* p = new uint8_t[length];
+    hunks_.push_back(p);
+    return p;
+  }
+
+ private:
+  std::vector<uint8_t*> hunks_;
+};
+
+const struct {
+  const char* tag;
+  bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
+  bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
+  bool (*should_serialise)(ots::OpenTypeFile *file);
+  void (*free)(ots::OpenTypeFile *file);
+  bool required;
+} table_parsers[] = {
+  { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
+    ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
+  { "head", ots::ots_head_parse, ots::ots_head_serialise,
+    ots::ots_head_should_serialise, ots::ots_head_free, true },
+  { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
+    ots::ots_os2_should_serialise, ots::ots_os2_free, true },
+  { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
+    ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
+  { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
+    ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
+  { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+    ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
+  { "name", ots::ots_name_parse, ots::ots_name_serialise,
+    ots::ots_name_should_serialise, ots::ots_name_free, true },
+  { "post", ots::ots_post_parse, ots::ots_post_serialise,
+    ots::ots_post_should_serialise, ots::ots_post_free, true },
+  { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
+    ots::ots_loca_should_serialise, ots::ots_loca_free, false },
+  { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
+    ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
+  { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
+    ots::ots_cff_should_serialise, ots::ots_cff_free, false },
+  { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+    ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
+  { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+    ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
+  { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
+    ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
+  { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
+    ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
+  { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+    ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
+  { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
+    ots::ots_prep_should_serialise, ots::ots_prep_free, false },
+  { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+    ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
+  { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
+    ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
+  { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
+    ots::ots_kern_should_serialise, ots::ots_kern_free, false },
+  // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
+  // because they could refer GDEF table.
+  { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
+    ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
+  { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
+    ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
+  { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
+    ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
+  { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
+    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
+  { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
+  { "MATH", ots::ots_math_parse, ots::ots_math_serialise,
+    ots::ots_math_should_serialise, ots::ots_math_free, false },
+  // TODO(bashi): Support mort, base, and jstf tables.
+  { 0, NULL, NULL, NULL, NULL, false },
+};
+
+bool ProcessGeneric(ots::OpenTypeFile *header,
+                    uint32_t signature,
+                    ots::OTSStream *output,
+                    const uint8_t *data, size_t length,
+                    const std::vector<OpenTypeTable>& tables,
+                    ots::Buffer& file);
+
+bool ProcessTTF(ots::OpenTypeFile *header,
+                ots::OTSStream *output, const uint8_t *data, size_t length) {
+  ots::Buffer file(data, length);
+
+  // we disallow all files > 1GB in size for sanity.
+  if (length > 1024 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE_MSG_HDR("error reading version tag");
+  }
+  if (!ots::IsValidVersionTag(header->version)) {
+      return OTS_FAILURE_MSG_HDR("invalid version tag");
+  }
+
+  if (!file.ReadU16(&header->num_tables) ||
+      !file.ReadU16(&header->search_range) ||
+      !file.ReadU16(&header->entry_selector) ||
+      !file.ReadU16(&header->range_shift)) {
+    return OTS_FAILURE_MSG_HDR("error reading table directory search header");
+  }
+
+  // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
+  // overflow num_tables is, at most, 2^16 / 16 = 2^12
+  if (header->num_tables >= 4096 || header->num_tables < 1) {
+    return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
+  }
+
+  unsigned max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= header->num_tables) {
+    max_pow2++;
+  }
+  const uint16_t expected_search_range = (1u << max_pow2) << 4;
+
+  // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
+  // http://www.princexml.com/fonts/ have unexpected search_range value.
+  if (header->search_range != expected_search_range) {
+    OTS_FAILURE_MSG_HDR("bad search range");
+    header->search_range = expected_search_range;  // Fix the value.
+  }
+
+  // entry_selector is Log2(maximum power of 2 <= numTables)
+  if (header->entry_selector != max_pow2) {
+    return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
+  }
+
+  // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
+  // doesn't over flow because we range checked it above. Also, we know that
+  // it's > header->search_range by construction of search_range.
+  const uint16_t expected_range_shift =
+      16 * header->num_tables - header->search_range;
+  if (header->range_shift != expected_range_shift) {
+    OTS_FAILURE_MSG_HDR("bad range shift");
+    header->range_shift = expected_range_shift;  // the same as above.
+  }
+
+  // Next up is the list of tables.
+  std::vector<OpenTypeTable> tables;
+
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    OpenTypeTable table;
+    if (!file.ReadTag(&table.tag) ||
+        !file.ReadU32(&table.chksum) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length)) {
+      return OTS_FAILURE_MSG_HDR("error reading table directory");
+    }
+
+    table.uncompressed_length = table.length;
+    tables.push_back(table);
+  }
+
+  return ProcessGeneric(header, header->version, output, data, length,
+                        tables, file);
+}
+
+bool ProcessWOFF(ots::OpenTypeFile *header,
+                 ots::OTSStream *output, const uint8_t *data, size_t length) {
+  ots::Buffer file(data, length);
+
+  // we disallow all files > 1GB in size for sanity.
+  if (length > 1024 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+  }
+
+  uint32_t woff_tag;
+  if (!file.ReadTag(&woff_tag)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
+  }
+
+  if (woff_tag != Tag("wOFF")) {
+    return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE_MSG_HDR("error reading version tag");
+  }
+  if (!ots::IsValidVersionTag(header->version)) {
+    return OTS_FAILURE_MSG_HDR("invalid version tag");
+  }
+
+  header->search_range = 0;
+  header->entry_selector = 0;
+  header->range_shift = 0;
+
+  uint32_t reported_length;
+  if (!file.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
+  }
+
+  if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
+    return OTS_FAILURE_MSG_HDR("error reading number of tables");
+  }
+
+  uint16_t reserved_value;
+  if (!file.ReadU16(&reserved_value) || reserved_value) {
+    return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
+  }
+
+  uint32_t reported_total_sfnt_size;
+  if (!file.ReadU32(&reported_total_sfnt_size)) {
+    return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
+  }
+
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  if (!file.Skip(2 * 2)) {
+    return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields");
+  }
+
+  // Checks metadata block size.
+  uint32_t meta_offset;
+  uint32_t meta_length;
+  uint32_t meta_length_orig;
+  if (!file.ReadU32(&meta_offset) ||
+      !file.ReadU32(&meta_length) ||
+      !file.ReadU32(&meta_length_orig)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+  }
+  if (meta_offset) {
+    if (meta_offset >= length || length - meta_offset < meta_length) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block location/size");
+    }
+  }
+
+  // Checks private data block size.
+  uint32_t priv_offset;
+  uint32_t priv_length;
+  if (!file.ReadU32(&priv_offset) ||
+      !file.ReadU32(&priv_length)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+  }
+  if (priv_offset) {
+    if (priv_offset >= length || length - priv_offset < priv_length) {
+      return OTS_FAILURE_MSG_HDR("invalid private block location/size");
+    }
+  }
+
+  // Next up is the list of tables.
+  std::vector<OpenTypeTable> tables;
+
+  uint32_t first_index = 0;
+  uint32_t last_index = 0;
+  // Size of sfnt header plus size of table records.
+  uint64_t total_sfnt_size = 12 + 16 * header->num_tables;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    OpenTypeTable table;
+    if (!file.ReadTag(&table.tag) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length) ||
+        !file.ReadU32(&table.uncompressed_length) ||
+        !file.ReadU32(&table.chksum)) {
+      return OTS_FAILURE_MSG_HDR("error reading table directory");
+    }
+
+    total_sfnt_size += ots::Round4(table.uncompressed_length);
+    if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("sfnt size overflow");
+    }
+    tables.push_back(table);
+    if (i == 0 || tables[first_index].offset > table.offset)
+      first_index = i;
+    if (i == 0 || tables[last_index].offset < table.offset)
+      last_index = i;
+  }
+
+  if (reported_total_sfnt_size != total_sfnt_size) {
+    return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
+  }
+
+  // Table data must follow immediately after the header.
+  if (tables[first_index].offset != ots::Round4(file.offset())) {
+    return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
+  }
+
+  if (tables[last_index].offset >= length ||
+      length - tables[last_index].offset < tables[last_index].length) {
+    return OTS_FAILURE_MSG_HDR("invalid table location/size");
+  }
+  // Blocks must follow immediately after the previous block.
+  // (Except for padding with a maximum of three null bytes)
+  uint64_t block_end = ots::Round4(
+      static_cast<uint64_t>(tables[last_index].offset) +
+      static_cast<uint64_t>(tables[last_index].length));
+  if (block_end > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE_MSG_HDR("invalid table location/size");
+  }
+  if (meta_offset) {
+    if (block_end != meta_offset) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block location");
+    }
+    block_end = ots::Round4(static_cast<uint64_t>(meta_offset) +
+                            static_cast<uint64_t>(meta_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block size");
+    }
+  }
+  if (priv_offset) {
+    if (block_end != priv_offset) {
+      return OTS_FAILURE_MSG_HDR("invalid private block location");
+    }
+    block_end = ots::Round4(static_cast<uint64_t>(priv_offset) +
+                            static_cast<uint64_t>(priv_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("invalid private block size");
+    }
+  }
+  if (block_end != ots::Round4(length)) {
+    return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
+  }
+
+  return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
+}
+
+bool ProcessWOFF2(ots::OpenTypeFile *header,
+                  ots::OTSStream *output, const uint8_t *data, size_t length) {
+  size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
+  if (decompressed_size == 0) {
+    return OTS_FAILURE();
+  }
+  // decompressed font must be <= 30MB
+  if (decompressed_size > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<uint8_t> decompressed_buffer(decompressed_size);
+  if (!ots::ConvertWOFF2ToTTF(header, &decompressed_buffer[0], decompressed_size,
+                              data, length)) {
+    return OTS_FAILURE();
+  }
+  return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
+}
+
+ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
+  ots::TableAction action = ots::TABLE_ACTION_DEFAULT;
+
+  action = header->context->GetTableAction(htonl(tag));
+
+  if (action == ots::TABLE_ACTION_DEFAULT) {
+    action = ots::TABLE_ACTION_DROP;
+
+    for (unsigned i = 0; ; ++i) {
+      if (table_parsers[i].parse == NULL) break;
+
+      if (Tag(table_parsers[i].tag) == tag) {
+        action = ots::TABLE_ACTION_SANITIZE;
+        break;
+      }
+    }
+  }
+
+  assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this.
+  return action;
+}
+
+bool GetTableData(const uint8_t *data,
+                  const OpenTypeTable table,
+                  Arena *arena,
+                  size_t *table_length,
+                  const uint8_t **table_data) {
+  if (table.uncompressed_length != table.length) {
+    // Compressed table. Need to uncompress into memory first.
+    *table_length = table.uncompressed_length;
+    *table_data = (*arena).Allocate(*table_length);
+    uLongf dest_len = *table_length;
+    int r = uncompress((Bytef*) *table_data, &dest_len,
+                       data + table.offset, table.length);
+    if (r != Z_OK || dest_len != *table_length) {
+      return false;
+    }
+  } else {
+    // Uncompressed table. We can process directly from memory.
+    *table_data = data + table.offset;
+    *table_length = table.length;
+  }
+
+  return true;
+}
+
+bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
+                    ots::OTSStream *output,
+                    const uint8_t *data, size_t length,
+                    const std::vector<OpenTypeTable>& tables,
+                    ots::Buffer& file) {
+  const size_t data_offset = file.offset();
+
+  uint32_t uncompressed_sum = 0;
+
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    // the tables must be sorted by tag (when taken as big-endian numbers).
+    // This also remove the possibility of duplicate tables.
+    if (i) {
+      const uint32_t this_tag = ntohl(tables[i].tag);
+      const uint32_t prev_tag = ntohl(tables[i - 1].tag);
+      if (this_tag <= prev_tag) {
+        return OTS_FAILURE_MSG_HDR("table directory not correctly ordered");
+      }
+    }
+
+    // all tag names must be built from printable ASCII characters
+    if (!CheckTag(tables[i].tag)) {
+      return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag);
+    }
+
+    // tables must be 4-byte aligned
+    if (tables[i].offset & 3) {
+      return OTS_FAILURE_MSG_TAG("misaligned table", &tables[i].tag);
+    }
+
+    // and must be within the file
+    if (tables[i].offset < data_offset || tables[i].offset >= length) {
+      return OTS_FAILURE_MSG_TAG("invalid table offset", &tables[i].tag);
+    }
+    // disallow all tables with a zero length
+    if (tables[i].length < 1) {
+      // Note: malayalam.ttf has zero length CVT table...
+      return OTS_FAILURE_MSG_TAG("zero-length table", &tables[i].tag);
+    }
+    // disallow all tables with a length > 1GB
+    if (tables[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables[i].tag);
+    }
+    // disallow tables where the uncompressed size is < the compressed size.
+    if (tables[i].uncompressed_length < tables[i].length) {
+      return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables[i].tag);
+    }
+    if (tables[i].uncompressed_length > tables[i].length) {
+      // We'll probably be decompressing this table.
+
+      // disallow all tables which uncompress to > 30 MB
+      if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
+        return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables[i].tag);
+      }
+      if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
+        return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables[i].tag);
+      }
+
+      uncompressed_sum += tables[i].uncompressed_length;
+    }
+    // since we required that the file be < 1GB in length, and that the table
+    // length is < 1GB, the following addtion doesn't overflow
+    uint32_t end_byte = tables[i].offset + tables[i].length;
+    // Tables in the WOFF file must be aligned 4-byte boundary.
+    if (signature == Tag("wOFF")) {
+        end_byte = ots::Round4(end_byte);
+    }
+    if (!end_byte || end_byte > length) {
+      return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag);
+    }
+  }
+
+  // All decompressed tables uncompressed must be <= 30MB.
+  if (uncompressed_sum > 30 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
+  }
+
+  std::map<uint32_t, OpenTypeTable> table_map;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    table_map[tables[i].tag] = tables[i];
+  }
+
+  // check that the tables are not overlapping.
+  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */));
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset + tables[i].length,
+                       static_cast<uint8_t>(0) /* end */));
+  }
+  std::sort(overlap_checker.begin(), overlap_checker.end());
+  int overlap_count = 0;
+  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+    overlap_count += (overlap_checker[i].second ? 1 : -1);
+    if (overlap_count > 1) {
+      return OTS_FAILURE_MSG_HDR("overlapping tables");
+    }
+  }
+
+  Arena arena;
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+
+    uint32_t tag = Tag(table_parsers[i].tag);
+    const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
+
+    ots::TableAction action = GetTableAction(header, tag);
+    if (it == table_map.end()) {
+      if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
+        return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
+      }
+      continue;
+    }
+
+    const uint8_t* table_data;
+    size_t table_length;
+
+    if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+      return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
+    }
+
+    if (action == ots::TABLE_ACTION_SANITIZE &&
+        !table_parsers[i].parse(header, table_data, table_length)) {
+      // TODO: parsers should generate specific messages detailing the failure;
+      // once those are all added, we won't need a generic failure message here
+      return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag);
+    }
+  }
+
+  if (header->cff) {
+    // font with PostScript glyph
+    if (header->version != Tag("OTTO")) {
+      return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
+    }
+    if (header->glyf || header->loca) {
+      // mixing outline formats is not recommended
+      return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
+    }
+  } else {
+    if (!header->glyf || !header->loca) {
+      // No TrueType glyph found.
+      // Note: bitmap-only fonts are not supported.
+      return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present");
+    }
+  }
+
+  uint16_t num_output_tables = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (table_parsers[i].should_serialise(header)) {
+      num_output_tables++;
+    }
+  }
+
+  for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+       it != table_map.end(); ++it) {
+    ots::TableAction action = GetTableAction(header, it->first);
+    if (action == ots::TABLE_ACTION_PASSTHRU) {
+      num_output_tables++;
+    }
+  }
+
+  uint16_t max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_output_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+
+  // most of the errors here are highly unlikely - they'd only occur if the
+  // output stream returns a failure, e.g. lack of space to write
+  output->ResetChecksum();
+  if (!output->WriteTag(header->version) ||
+      !output->WriteU16(num_output_tables) ||
+      !output->WriteU16(output_search_range) ||
+      !output->WriteU16(max_pow2) ||
+      !output->WriteU16((num_output_tables << 4) - output_search_range)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+  const uint32_t offset_table_chksum = output->chksum();
+
+  const size_t table_record_offset = output->Tell();
+  if (!output->Pad(16 * num_output_tables)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  std::vector<OutputTable> out_tables;
+
+  size_t head_table_offset = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (!table_parsers[i].should_serialise(header)) {
+      continue;
+    }
+
+    OutputTable out;
+    uint32_t tag = Tag(table_parsers[i].tag);
+    out.tag = tag;
+    out.offset = output->Tell();
+
+    output->ResetChecksum();
+    if (tag == Tag("head")) {
+      head_table_offset = out.offset;
+    }
+    if (!table_parsers[i].serialise(output, header)) {
+      return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
+    }
+
+    const size_t end_offset = output->Tell();
+    if (end_offset <= out.offset) {
+      // paranoid check. |end_offset| is supposed to be greater than the offset,
+      // as long as the Tell() interface is implemented correctly.
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    out.length = end_offset - out.offset;
+
+    // align tables to four bytes
+    if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    out.chksum = output->chksum();
+    out_tables.push_back(out);
+  }
+
+  for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+       it != table_map.end(); ++it) {
+    ots::TableAction action = GetTableAction(header, it->first);
+    if (action == ots::TABLE_ACTION_PASSTHRU) {
+      OutputTable out;
+      out.tag = it->second.tag;
+      out.offset = output->Tell();
+
+      output->ResetChecksum();
+      if (it->second.tag == Tag("head")) {
+        head_table_offset = out.offset;
+      }
+
+      const uint8_t* table_data;
+      size_t table_length;
+
+      if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+        return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
+      }
+
+      if (!output->Write(table_data, table_length)) {
+        return OTS_FAILURE_MSG_HDR("Failed to serialize table");
+      }
+
+      const size_t end_offset = output->Tell();
+      if (end_offset <= out.offset) {
+        // paranoid check. |end_offset| is supposed to be greater than the offset,
+        // as long as the Tell() interface is implemented correctly.
+        return OTS_FAILURE_MSG_HDR("error writing output");
+      }
+      out.length = end_offset - out.offset;
+
+      // align tables to four bytes
+      if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+        return OTS_FAILURE_MSG_HDR("error writing output");
+      }
+      out.chksum = output->chksum();
+      out_tables.push_back(out);
+    }
+  }
+
+  const size_t end_of_file = output->Tell();
+
+  // Need to sort the output tables for inclusion in the file
+  std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
+  if (!output->Seek(table_record_offset)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  output->ResetChecksum();
+  uint32_t tables_chksum = 0;
+  for (unsigned i = 0; i < out_tables.size(); ++i) {
+    if (!output->WriteTag(out_tables[i].tag) ||
+        !output->WriteU32(out_tables[i].chksum) ||
+        !output->WriteU32(out_tables[i].offset) ||
+        !output->WriteU32(out_tables[i].length)) {
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    tables_chksum += out_tables[i].chksum;
+  }
+  const uint32_t table_record_chksum = output->chksum();
+
+  // http://www.microsoft.com/typography/otspec/otff.htm
+  const uint32_t file_chksum
+      = offset_table_chksum + tables_chksum + table_record_chksum;
+  const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
+
+  // seek into the 'head' table and write in the checksum magic value
+  if (!head_table_offset) {
+    return OTS_FAILURE_MSG_HDR("internal error!");
+  }
+  if (!output->Seek(head_table_offset + 8)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+  if (!output->WriteU32(chksum_magic)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  if (!output->Seek(end_of_file)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+void EnableWOFF2() {
+}
+
+bool IsValidVersionTag(uint32_t tag) {
+  return tag == Tag("\x00\x01\x00\x00") ||
+         // OpenType fonts with CFF data have 'OTTO' tag.
+         tag == Tag("OTTO") ||
+         // Older Mac fonts might have 'true' or 'typ1' tag.
+         tag == Tag("true") ||
+         tag == Tag("typ1");
+}
+
+bool OTSContext::Process(OTSStream *output,
+                         const uint8_t *data,
+                         size_t length) {
+  OpenTypeFile header;
+
+  header.context = this;
+
+  if (length < 4) {
+    return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
+  }
+
+  bool result;
+  if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
+    result = ProcessWOFF(&header, output, data, length);
+  } else if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') {
+    result = ProcessWOFF2(&header, output, data, length);
+  } else {
+    result = ProcessTTF(&header, output, data, length);
+  }
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+    table_parsers[i].free(&header);
+  }
+  return result;
+}
+
+// For backward compatibility
+bool Process(OTSStream *output, const uint8_t *data, size_t length) {
+  static OTSContext context;
+  return context.Process(output, data, length);
+}
+
+}  // namespace ots
diff --git a/third_party/ots/src/ots.h b/third_party/ots/src/ots.h
new file mode 100644
index 0000000..ba3ba77
--- /dev/null
+++ b/third_party/ots/src/ots.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_H_
+#define OTS_H_
+
+#include <stddef.h>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+
+#include "opentype-sanitiser.h"
+
+// arraysize borrowed from base/basictypes.h
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+namespace ots {
+
+#if !defined(OTS_DEBUG)
+#define OTS_FAILURE() false
+#else
+#define OTS_FAILURE() \
+  (\
+    std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \
+                 __FILE__, __LINE__, __FUNCTION__) \
+    && false\
+  )
+#endif
+
+// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
+// message-less OTS_FAILURE(), so that the current parser will return 'false' as
+// its result (indicating a failure).
+
+#if !defined(OTS_DEBUG)
+#define OTS_MESSAGE_(level,otf_,...) \
+  (otf_)->context->Message(level,__VA_ARGS__)
+#else
+#define OTS_MESSAGE_(level,otf_,...) \
+  OTS_FAILURE(), \
+  (otf_)->context->Message(level,__VA_ARGS__)
+#endif
+
+// Generate a simple message
+#define OTS_FAILURE_MSG_(otf_,...) \
+  (OTS_MESSAGE_(0,otf_,__VA_ARGS__), false)
+
+#define OTS_WARNING_MSG_(otf_,...) \
+  OTS_MESSAGE_(1,otf_,__VA_ARGS__)
+
+// Generate a message with an associated table tag
+#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
+  (OTS_MESSAGE_(0,otf_,"%4.4s: %s", tag_, msg_), false)
+
+// Convenience macros for use in files that only handle a single table tag,
+// defined as TABLE_NAME at the top of the file; the 'file' variable is
+// expected to be the current OpenTypeFile pointer.
+#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
+
+#define OTS_WARNING(...) OTS_WARNING_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
+
+// -----------------------------------------------------------------------------
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+// -----------------------------------------------------------------------------
+class Buffer {
+ public:
+  Buffer(const uint8_t *buf, size_t len)
+      : buffer_(buf),
+        length_(len),
+        offset_(0) { }
+
+  bool Skip(size_t n_bytes) {
+    return Read(NULL, n_bytes);
+  }
+
+  bool Read(uint8_t *buf, size_t n_bytes) {
+    if (n_bytes > 1024 * 1024 * 1024) {
+      return OTS_FAILURE();
+    }
+    if ((offset_ + n_bytes > length_) ||
+        (offset_ > length_ - n_bytes)) {
+      return OTS_FAILURE();
+    }
+    if (buf) {
+      std::memcpy(buf, buffer_ + offset_, n_bytes);
+    }
+    offset_ += n_bytes;
+    return true;
+  }
+
+  inline bool ReadU8(uint8_t *value) {
+    if (offset_ + 1 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = buffer_[offset_];
+    ++offset_;
+    return true;
+  }
+
+  bool ReadU16(uint16_t *value) {
+    if (offset_ + 2 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+    *value = ntohs(*value);
+    offset_ += 2;
+    return true;
+  }
+
+  bool ReadS16(int16_t *value) {
+    return ReadU16(reinterpret_cast<uint16_t*>(value));
+  }
+
+  bool ReadU24(uint32_t *value) {
+    if (offset_ + 3 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
+        static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
+        static_cast<uint32_t>(buffer_[offset_ + 2]);
+    offset_ += 3;
+    return true;
+  }
+
+  bool ReadU32(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    *value = ntohl(*value);
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadS32(int32_t *value) {
+    return ReadU32(reinterpret_cast<uint32_t*>(value));
+  }
+
+  bool ReadTag(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadR64(uint64_t *value) {
+    if (offset_ + 8 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
+    offset_ += 8;
+    return true;
+  }
+
+  const uint8_t *buffer() const { return buffer_; }
+  size_t offset() const { return offset_; }
+  size_t length() const { return length_; }
+
+  void set_offset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+  const uint8_t * const buffer_;
+  const size_t length_;
+  size_t offset_;
+};
+
+// Round a value up to the nearest multiple of 4. Don't round the value in the
+// case that rounding up overflows.
+template<typename T> T Round4(T value) {
+  if (std::numeric_limits<T>::max() - value < 3) {
+    return value;
+  }
+  return (value + 3) & ~3;
+}
+
+template<typename T> T Round2(T value) {
+  if (value == std::numeric_limits<T>::max()) {
+    return value;
+  }
+  return (value + 1) & ~1;
+}
+
+bool IsValidVersionTag(uint32_t tag);
+
+#define FOR_EACH_TABLE_TYPE \
+  F(cff, CFF) \
+  F(cmap, CMAP) \
+  F(cvt, CVT) \
+  F(fpgm, FPGM) \
+  F(gasp, GASP) \
+  F(gdef, GDEF) \
+  F(glyf, GLYF) \
+  F(gpos, GPOS) \
+  F(gsub, GSUB) \
+  F(hdmx, HDMX) \
+  F(head, HEAD) \
+  F(hhea, HHEA) \
+  F(hmtx, HMTX) \
+  F(kern, KERN) \
+  F(loca, LOCA) \
+  F(ltsh, LTSH) \
+  F(math, MATH) \
+  F(maxp, MAXP) \
+  F(name, NAME) \
+  F(os2, OS2) \
+  F(post, POST) \
+  F(prep, PREP) \
+  F(vdmx, VDMX) \
+  F(vorg, VORG) \
+  F(vhea, VHEA) \
+  F(vmtx, VMTX)
+
+#define F(name, capname) struct OpenType##capname;
+FOR_EACH_TABLE_TYPE
+#undef F
+
+struct OpenTypeFile {
+  OpenTypeFile() {
+#define F(name, capname) name = NULL;
+    FOR_EACH_TABLE_TYPE
+#undef F
+  }
+
+  uint32_t version;
+  uint16_t num_tables;
+  uint16_t search_range;
+  uint16_t entry_selector;
+  uint16_t range_shift;
+
+  OTSContext *context;
+
+#define F(name, capname) OpenType##capname *name;
+FOR_EACH_TABLE_TYPE
+#undef F
+};
+
+#define F(name, capname) \
+bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+bool ots_##name##_should_serialise(OpenTypeFile *f); \
+bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
+void ots_##name##_free(OpenTypeFile *f);
+// TODO(yusukes): change these function names to follow Chromium coding rule.
+FOR_EACH_TABLE_TYPE
+#undef F
+
+}  // namespace ots
+
+#endif  // OTS_H_
diff --git a/third_party/ots/src/post.cc b/third_party/ots/src/post.cc
new file mode 100644
index 0000000..338d869
--- /dev/null
+++ b/third_party/ots/src/post.cc
@@ -0,0 +1,188 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "post.h"
+
+#include "maxp.h"
+
+// post - PostScript
+// http://www.microsoft.com/typography/otspec/post.htm
+
+#define TABLE_NAME "post"
+
+namespace ots {
+
+bool ots_post_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypePOST *post = new OpenTypePOST;
+  file->post = post;
+
+  if (!table.ReadU32(&post->version) ||
+      !table.ReadU32(&post->italic_angle) ||
+      !table.ReadS16(&post->underline) ||
+      !table.ReadS16(&post->underline_thickness) ||
+      !table.ReadU32(&post->is_fixed_pitch)) {
+    return OTS_FAILURE_MSG("Failed to read post header");
+  }
+
+  if (post->underline_thickness < 0) {
+    post->underline_thickness = 1;
+  }
+
+  if (post->version == 0x00010000) {
+    return true;
+  } else if (post->version == 0x00030000) {
+    return true;
+  } else if (post->version != 0x00020000) {
+    // 0x00025000 is deprecated. We don't accept it.
+    return OTS_FAILURE_MSG("Bad post version %x", post->version);
+  }
+
+  // We have a version 2 table with a list of Pascal strings at the end
+
+  // We don't care about the memory usage fields. We'll set all these to zero
+  // when serialising
+  if (!table.Skip(16)) {
+    return OTS_FAILURE_MSG("Failed to skip memory usage in post table");
+  }
+
+  uint16_t num_glyphs = 0;
+  if (!table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read number of glyphs");
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table required by post table");
+  }
+
+  if (num_glyphs == 0) {
+    if (file->maxp->num_glyphs > 258) {
+      return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 glyphs in the font");
+    }
+    OTS_WARNING("table version is 1, but no glyf names are found");
+    // workaround for fonts in http://www.fontsquirrel.com/fontface
+    // (e.g., yataghan.ttf).
+    post->version = 0x00010000;
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
+    return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs);
+  }
+
+  post->glyph_name_index.resize(num_glyphs);
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    if (!table.ReadU16(&post->glyph_name_index[i])) {
+      return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i);
+    }
+    // Note: A strict interpretation of the specification requires name indexes
+    // are less than 32768. This, however, excludes fonts like unifont.ttf
+    // which cover all of unicode.
+  }
+
+  // Now we have an array of Pascal strings. We have to check that they are all
+  // valid and read them in.
+  const size_t strings_offset = table.offset();
+  const uint8_t *strings = data + strings_offset;
+  const uint8_t *strings_end = data + length;
+
+  for (;;) {
+    if (strings == strings_end) break;
+    const unsigned string_length = *strings;
+    if (strings + 1 + string_length > strings_end) {
+      return OTS_FAILURE_MSG("Bad string length %d", string_length);
+    }
+    if (std::memchr(strings + 1, '\0', string_length)) {
+      return OTS_FAILURE_MSG("Bad string of length %d", string_length);
+    }
+    post->names.push_back(
+        std::string(reinterpret_cast<const char*>(strings + 1), string_length));
+    strings += 1 + string_length;
+  }
+  const unsigned num_strings = post->names.size();
+
+  // check that all the references are within bounds
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    unsigned offset = post->glyph_name_index[i];
+    if (offset < 258) {
+      continue;
+    }
+
+    offset -= 258;
+    if (offset >= num_strings) {
+      return OTS_FAILURE_MSG("Bad string index %d", offset);
+    }
+  }
+
+  return true;
+}
+
+bool ots_post_should_serialise(OpenTypeFile *file) {
+  return file->post != NULL;
+}
+
+bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePOST *post = file->post;
+
+  // OpenType with CFF glyphs must have v3 post table.
+  if (file->post && file->cff && file->post->version != 0x00030000) {
+    return OTS_FAILURE_MSG("Bad post version %x", post->version);
+  }
+
+  if (!out->WriteU32(post->version) ||
+      !out->WriteU32(post->italic_angle) ||
+      !out->WriteS16(post->underline) ||
+      !out->WriteS16(post->underline_thickness) ||
+      !out->WriteU32(post->is_fixed_pitch) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0)) {
+    return OTS_FAILURE_MSG("Failed to write post header");
+  }
+
+  if (post->version != 0x00020000) {
+    return true;  // v1.0 and v3.0 does not have glyph names.
+  }
+
+  const uint16_t num_indexes =
+      static_cast<uint16_t>(post->glyph_name_index.size());
+  if (num_indexes != post->glyph_name_index.size() ||
+      !out->WriteU16(num_indexes)) {
+    return OTS_FAILURE_MSG("Failed to write number of indices");
+  }
+
+  for (uint16_t i = 0; i < num_indexes; ++i) {
+    if (!out->WriteU16(post->glyph_name_index[i])) {
+      return OTS_FAILURE_MSG("Failed to write name index %d", i);
+    }
+  }
+
+  // Now we just have to write out the strings in the correct order
+  for (unsigned i = 0; i < post->names.size(); ++i) {
+    const std::string& s = post->names[i];
+    const uint8_t string_length = static_cast<uint8_t>(s.size());
+    if (string_length != s.size() ||
+        !out->Write(&string_length, 1)) {
+      return OTS_FAILURE_MSG("Failed to write string %d", i);
+    }
+    // Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
+    // We allow them.
+    if (string_length > 0 && !out->Write(s.data(), string_length)) {
+      return OTS_FAILURE_MSG("Failed to write string length for string %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_post_free(OpenTypeFile *file) {
+  delete file->post;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/post.h b/third_party/ots/src/post.h
new file mode 100644
index 0000000..f220d4f
--- /dev/null
+++ b/third_party/ots/src/post.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_POST_H_
+#define OTS_POST_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct OpenTypePOST {
+  uint32_t version;
+  uint32_t italic_angle;
+  int16_t underline;
+  int16_t underline_thickness;
+  uint32_t is_fixed_pitch;
+
+  std::vector<uint16_t> glyph_name_index;
+  std::vector<std::string> names;
+};
+
+}  // namespace ots
+
+#endif  // OTS_POST_H_
diff --git a/third_party/ots/src/prep.cc b/third_party/ots/src/prep.cc
new file mode 100644
index 0000000..ea3dec0
--- /dev/null
+++ b/third_party/ots/src/prep.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "prep.h"
+
+// prep - Control Value Program
+// http://www.microsoft.com/typography/otspec/prep.htm
+
+#define TABLE_NAME "prep"
+
+namespace ots {
+
+bool ots_prep_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypePREP *prep = new OpenTypePREP;
+  file->prep = prep;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("table length %ld > 120K", length);  // almost all prep tables are less than 9k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Failed to read table of length %ld", length);
+  }
+
+  prep->data = data;
+  prep->length = length;
+  return true;
+}
+
+bool ots_prep_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->prep != NULL;
+}
+
+bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePREP *prep = file->prep;
+
+  if (!out->Write(prep->data, prep->length)) {
+    return OTS_FAILURE_MSG("Failed to write table length");
+  }
+
+  return true;
+}
+
+void ots_prep_free(OpenTypeFile *file) {
+  delete file->prep;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/prep.h b/third_party/ots/src/prep.h
new file mode 100644
index 0000000..935ca11
--- /dev/null
+++ b/third_party/ots/src/prep.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_PREP_H_
+#define OTS_PREP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypePREP {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_PREP_H_
diff --git a/third_party/ots/src/vdmx.cc b/third_party/ots/src/vdmx.cc
new file mode 100644
index 0000000..4853d63
--- /dev/null
+++ b/third_party/ots/src/vdmx.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vdmx.h"
+
+// VDMX - Vertical Device Metrics
+// http://www.microsoft.com/typography/otspec/vdmx.htm
+
+#define TABLE_NAME "VDMX"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->vdmx; \
+    file->vdmx = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->vdmx = new OpenTypeVDMX;
+  OpenTypeVDMX * const vdmx = file->vdmx;
+
+  if (!table.ReadU16(&vdmx->version) ||
+      !table.ReadU16(&vdmx->num_recs) ||
+      !table.ReadU16(&vdmx->num_ratios)) {
+    return OTS_FAILURE_MSG("Failed to read table header");
+  }
+
+  if (vdmx->version > 1) {
+    DROP_THIS_TABLE("bad version: %u", vdmx->version);
+    return true;  // continue transcoding
+  }
+
+  vdmx->rat_ranges.reserve(vdmx->num_ratios);
+  for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+    OpenTypeVDMXRatioRecord rec;
+
+    if (!table.ReadU8(&rec.charset) ||
+        !table.ReadU8(&rec.x_ratio) ||
+        !table.ReadU8(&rec.y_start_ratio) ||
+        !table.ReadU8(&rec.y_end_ratio)) {
+      return OTS_FAILURE_MSG("Failed to read ratio header %d", i);
+    }
+
+    if (rec.charset > 1) {
+      DROP_THIS_TABLE("bad charset: %u", rec.charset);
+      return true;
+    }
+
+    if (rec.y_start_ratio > rec.y_end_ratio) {
+      DROP_THIS_TABLE("bad y ratio");
+      return true;
+    }
+
+    // All values set to zero signal the default grouping to use;
+    // if present, this must be the last Ratio group in the table.
+    if ((i < vdmx->num_ratios - 1u) &&
+        (rec.x_ratio == 0) &&
+        (rec.y_start_ratio == 0) &&
+        (rec.y_end_ratio == 0)) {
+      // workaround for fonts which have 2 or more {0, 0, 0} terminators.
+      DROP_THIS_TABLE("superfluous terminator found");
+      return true;
+    }
+
+    vdmx->rat_ranges.push_back(rec);
+  }
+
+  vdmx->offsets.reserve(vdmx->num_ratios);
+  const size_t current_offset = table.offset();
+  // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
+  for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+    uint16_t offset;
+    if (!table.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read ratio offset %d", i);
+    }
+    if (current_offset + offset >= length) {  // thus doesn't overflow.
+      return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i);
+    }
+
+    vdmx->offsets.push_back(offset);
+  }
+
+  vdmx->groups.reserve(vdmx->num_recs);
+  for (unsigned i = 0; i < vdmx->num_recs; ++i) {
+    OpenTypeVDMXGroup group;
+    if (!table.ReadU16(&group.recs) ||
+        !table.ReadU8(&group.startsz) ||
+        !table.ReadU8(&group.endsz)) {
+      return OTS_FAILURE_MSG("Failed to read record header %d", i);
+    }
+    group.entries.reserve(group.recs);
+    for (unsigned j = 0; j < group.recs; ++j) {
+      OpenTypeVDMXVTable vt;
+      if (!table.ReadU16(&vt.y_pel_height) ||
+          !table.ReadS16(&vt.y_max) ||
+          !table.ReadS16(&vt.y_min)) {
+        return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j);
+      }
+      if (vt.y_max < vt.y_min) {
+        DROP_THIS_TABLE("bad y min/max");
+        return true;
+      }
+
+      // This table must appear in sorted order (sorted by yPelHeight),
+      // but need not be continuous.
+      if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
+        DROP_THIS_TABLE("the table is not sorted");
+        return true;
+      }
+
+      group.entries.push_back(vt);
+    }
+    vdmx->groups.push_back(group);
+  }
+
+  return true;
+}
+
+bool ots_vdmx_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->vdmx != NULL;
+}
+
+bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVDMX * const vdmx = file->vdmx;
+
+  if (!out->WriteU16(vdmx->version) ||
+      !out->WriteU16(vdmx->num_recs) ||
+      !out->WriteU16(vdmx->num_ratios)) {
+    return OTS_FAILURE_MSG("Failed to write table header");
+  }
+
+  for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
+    const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
+    if (!out->Write(&rec.charset, 1) ||
+        !out->Write(&rec.x_ratio, 1) ||
+        !out->Write(&rec.y_start_ratio, 1) ||
+        !out->Write(&rec.y_end_ratio, 1)) {
+      return OTS_FAILURE_MSG("Failed to write ratio %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
+    if (!out->WriteU16(vdmx->offsets[i])) {
+      return OTS_FAILURE_MSG("Failed to write ratio offset %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
+    const OpenTypeVDMXGroup& group = vdmx->groups[i];
+    if (!out->WriteU16(group.recs) ||
+        !out->Write(&group.startsz, 1) ||
+        !out->Write(&group.endsz, 1)) {
+      return OTS_FAILURE_MSG("Failed to write group %d", i);
+    }
+    for (unsigned j = 0; j < group.entries.size(); ++j) {
+      const OpenTypeVDMXVTable& vt = group.entries[j];
+      if (!out->WriteU16(vt.y_pel_height) ||
+          !out->WriteS16(vt.y_max) ||
+          !out->WriteS16(vt.y_min)) {
+        return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_vdmx_free(OpenTypeFile *file) {
+  delete file->vdmx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/vdmx.h b/third_party/ots/src/vdmx.h
new file mode 100644
index 0000000..1d959ef
--- /dev/null
+++ b/third_party/ots/src/vdmx.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VDMX_H_
+#define OTS_VDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVDMXRatioRecord {
+  uint8_t charset;
+  uint8_t x_ratio;
+  uint8_t y_start_ratio;
+  uint8_t y_end_ratio;
+};
+
+struct OpenTypeVDMXVTable {
+  uint16_t y_pel_height;
+  int16_t y_max;
+  int16_t y_min;
+};
+
+struct OpenTypeVDMXGroup {
+  uint16_t recs;
+  uint8_t startsz;
+  uint8_t endsz;
+  std::vector<OpenTypeVDMXVTable> entries;
+};
+
+struct OpenTypeVDMX {
+  uint16_t version;
+  uint16_t num_recs;
+  uint16_t num_ratios;
+  std::vector<OpenTypeVDMXRatioRecord> rat_ranges;
+  std::vector<uint16_t> offsets;
+  std::vector<OpenTypeVDMXGroup> groups;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VDMX_H_
diff --git a/third_party/ots/src/vhea.cc b/third_party/ots/src/vhea.cc
new file mode 100644
index 0000000..c689a83
--- /dev/null
+++ b/third_party/ots/src/vhea.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vhea.h"
+
+#include "gsub.h"
+#include "head.h"
+#include "maxp.h"
+
+// vhea - Vertical Header Table
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "vhea"
+
+namespace ots {
+
+bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVHEA *vhea = new OpenTypeVHEA;
+  file->vhea = vhea;
+
+  if (!table.ReadU32(&vhea->header.version)) {
+    return OTS_FAILURE_MSG("Failed to read version");
+  }
+  if (vhea->header.version != 0x00010000 &&
+      vhea->header.version != 0x00011000) {
+    return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version);
+  }
+
+  if (!ParseMetricsHeader(file, &table, &vhea->header)) {
+    return OTS_FAILURE_MSG("Failed to parse metrics in vhea");
+  }
+
+  return true;
+}
+
+bool ots_vhea_should_serialise(OpenTypeFile *file) {
+  // vhea should'nt serialise when vmtx doesn't exist.
+  // Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is
+  // preserved. See http://crbug.com/77386
+  return file->vhea != NULL && file->vmtx != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(file, out, &file->vhea->header)) {
+    return OTS_FAILURE_MSG("Failed to write vhea metrics");
+  }
+  return true;
+}
+
+void ots_vhea_free(OpenTypeFile *file) {
+  delete file->vhea;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/vhea.h b/third_party/ots/src/vhea.h
new file mode 100644
index 0000000..f8efde7
--- /dev/null
+++ b/third_party/ots/src/vhea.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VHEA_H_
+#define OTS_VHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VHEA_H_
+
diff --git a/third_party/ots/src/vmtx.cc b/third_party/ots/src/vmtx.cc
new file mode 100644
index 0000000..e29d32b
--- /dev/null
+++ b/third_party/ots/src/vmtx.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vmtx.h"
+
+#include "gsub.h"
+#include "maxp.h"
+#include "vhea.h"
+
+// vmtx - Vertical Metrics Table
+// http://www.microsoft.com/typography/otspec/vmtx.htm
+
+#define TABLE_NAME "vmtx"
+
+namespace ots {
+
+bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVMTX *vmtx = new OpenTypeVMTX;
+  file->vmtx = vmtx;
+
+  if (!file->vhea || !file->maxp) {
+    return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx");
+  }
+
+  if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs,
+                         &file->vhea->header, &vmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to parse vmtx metrics");
+  }
+
+  return true;
+}
+
+bool ots_vmtx_should_serialise(OpenTypeFile *file) {
+  // vmtx should serialise when vhea and GSUB are preserved.
+  // See the comment in ots_vhea_should_serialise().
+  return file->vmtx != NULL && file->vhea != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(file, out, &file->vmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to write vmtx metrics");
+  }
+  return true;
+}
+
+void ots_vmtx_free(OpenTypeFile *file) {
+  delete file->vmtx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/vmtx.h b/third_party/ots/src/vmtx.h
new file mode 100644
index 0000000..061dc73
--- /dev/null
+++ b/third_party/ots/src/vmtx.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VMTX_H_
+#define OTS_VMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VMTX_H_
+
diff --git a/third_party/ots/src/vorg.cc b/third_party/ots/src/vorg.cc
new file mode 100644
index 0000000..2662067
--- /dev/null
+++ b/third_party/ots/src/vorg.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vorg.h"
+
+#include <vector>
+
+// VORG - Vertical Origin Table
+// http://www.microsoft.com/typography/otspec/vorg.htm
+
+#define TABLE_NAME "VORG"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->vorg; \
+    file->vorg = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_vorg_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->vorg = new OpenTypeVORG;
+  OpenTypeVORG * const vorg = file->vorg;
+
+  uint16_t num_recs;
+  if (!table.ReadU16(&vorg->major_version) ||
+      !table.ReadU16(&vorg->minor_version) ||
+      !table.ReadS16(&vorg->default_vert_origin_y) ||
+      !table.ReadU16(&num_recs)) {
+    return OTS_FAILURE_MSG("Failed to read header");
+  }
+  if (vorg->major_version != 1) {
+    DROP_THIS_TABLE("bad major version: %u", vorg->major_version);
+    return true;
+  }
+  if (vorg->minor_version != 0) {
+    DROP_THIS_TABLE("bad minor version: %u", vorg->minor_version);
+    return true;
+  }
+
+  // num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
+  if (!num_recs) {
+    return true;
+  }
+
+  uint16_t last_glyph_index = 0;
+  vorg->metrics.reserve(num_recs);
+  for (unsigned i = 0; i < num_recs; ++i) {
+    OpenTypeVORGMetrics rec;
+
+    if (!table.ReadU16(&rec.glyph_index) ||
+        !table.ReadS16(&rec.vert_origin_y)) {
+      return OTS_FAILURE_MSG("Failed to read record %d", i);
+    }
+    if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
+      DROP_THIS_TABLE("the table is not sorted");
+      return true;
+    }
+    last_glyph_index = rec.glyph_index;
+
+    vorg->metrics.push_back(rec);
+  }
+
+  return true;
+}
+
+bool ots_vorg_should_serialise(OpenTypeFile *file) {
+  if (!file->cff) return false;  // this table is not for fonts with TT glyphs.
+  return file->vorg != NULL;
+}
+
+bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVORG * const vorg = file->vorg;
+  
+  const uint16_t num_metrics = static_cast<uint16_t>(vorg->metrics.size());
+  if (num_metrics != vorg->metrics.size() ||
+      !out->WriteU16(vorg->major_version) ||
+      !out->WriteU16(vorg->minor_version) ||
+      !out->WriteS16(vorg->default_vert_origin_y) ||
+      !out->WriteU16(num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to write table header");
+  }
+
+  for (uint16_t i = 0; i < num_metrics; ++i) {
+    const OpenTypeVORGMetrics& rec = vorg->metrics[i];
+    if (!out->WriteU16(rec.glyph_index) ||
+        !out->WriteS16(rec.vert_origin_y)) {
+      return OTS_FAILURE_MSG("Failed to write record %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_vorg_free(OpenTypeFile *file) {
+  delete file->vorg;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/vorg.h b/third_party/ots/src/vorg.h
new file mode 100644
index 0000000..c3d3ffd
--- /dev/null
+++ b/third_party/ots/src/vorg.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VORG_H_
+#define OTS_VORG_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVORGMetrics {
+  uint16_t glyph_index;
+  int16_t vert_origin_y;
+};
+
+struct OpenTypeVORG {
+  uint16_t major_version;
+  uint16_t minor_version;
+  int16_t default_vert_origin_y;
+  std::vector<OpenTypeVORGMetrics> metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VORG_H_
diff --git a/third_party/ots/src/woff2.cc b/third_party/ots/src/woff2.cc
new file mode 100644
index 0000000..31b562d
--- /dev/null
+++ b/third_party/ots/src/woff2.cc
@@ -0,0 +1,991 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the implementation of decompression of the proposed WOFF Ultra
+// Condensed file format.
+
+#include <cassert>
+#include <cstdlib>
+#include <vector>
+
+#include "third_party/brotli/src/brotli/dec/decode.h"
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+#include "ots.h"
+#include "woff2.h"
+
+#define TABLE_NAME "WOFF2"
+
+namespace {
+
+// simple glyph flags
+const uint8_t kGlyfOnCurve = 1 << 0;
+const uint8_t kGlyfXShort = 1 << 1;
+const uint8_t kGlyfYShort = 1 << 2;
+const uint8_t kGlyfRepeat = 1 << 3;
+const uint8_t kGlyfThisXIsSame = 1 << 4;
+const uint8_t kGlyfThisYIsSame = 1 << 5;
+
+// composite glyph flags
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int FLAG_WE_HAVE_A_SCALE = 1 << 3;
+const int FLAG_MORE_COMPONENTS = 1 << 5;
+const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+const size_t kSfntHeaderSize = 12;
+const size_t kSfntEntrySize = 16;
+const size_t kCheckSumAdjustmentOffset = 8;
+
+const size_t kEndPtsOfContoursOffset = 10;
+const size_t kCompositeGlyphBegin = 10;
+
+// Note that the byte order is big-endian, not the same as ots.cc
+#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
+
+const unsigned int kWoff2FlagsTransform = 1 << 5;
+
+const uint32_t kKnownTags[] = {
+  TAG('c', 'm', 'a', 'p'),  // 0
+  TAG('h', 'e', 'a', 'd'),  // 1
+  TAG('h', 'h', 'e', 'a'),  // 2
+  TAG('h', 'm', 't', 'x'),  // 3
+  TAG('m', 'a', 'x', 'p'),  // 4
+  TAG('n', 'a', 'm', 'e'),  // 5
+  TAG('O', 'S', '/', '2'),  // 6
+  TAG('p', 'o', 's', 't'),  // 7
+  TAG('c', 'v', 't', ' '),  // 8
+  TAG('f', 'p', 'g', 'm'),  // 9
+  TAG('g', 'l', 'y', 'f'),  // 10
+  TAG('l', 'o', 'c', 'a'),  // 11
+  TAG('p', 'r', 'e', 'p'),  // 12
+  TAG('C', 'F', 'F', ' '),  // 13
+  TAG('V', 'O', 'R', 'G'),  // 14
+  TAG('E', 'B', 'D', 'T'),  // 15
+  TAG('E', 'B', 'L', 'C'),  // 16
+  TAG('g', 'a', 's', 'p'),  // 17
+  TAG('h', 'd', 'm', 'x'),  // 18
+  TAG('k', 'e', 'r', 'n'),  // 19
+  TAG('L', 'T', 'S', 'H'),  // 20
+  TAG('P', 'C', 'L', 'T'),  // 21
+  TAG('V', 'D', 'M', 'X'),  // 22
+  TAG('v', 'h', 'e', 'a'),  // 23
+  TAG('v', 'm', 't', 'x'),  // 24
+  TAG('B', 'A', 'S', 'E'),  // 25
+  TAG('G', 'D', 'E', 'F'),  // 26
+  TAG('G', 'P', 'O', 'S'),  // 27
+  TAG('G', 'S', 'U', 'B'),  // 28
+  TAG('E', 'B', 'S', 'C'),  // 29
+  TAG('J', 'S', 'T', 'F'),  // 30
+  TAG('M', 'A', 'T', 'H'),  // 31
+  TAG('C', 'B', 'D', 'T'),  // 32
+  TAG('C', 'B', 'L', 'C'),  // 33
+  TAG('C', 'O', 'L', 'R'),  // 34
+  TAG('C', 'P', 'A', 'L'),  // 35
+  TAG('S', 'V', 'G', ' '),  // 36
+  TAG('s', 'b', 'i', 'x'),  // 37
+  TAG('a', 'c', 'n', 't'),  // 38
+  TAG('a', 'v', 'a', 'r'),  // 39
+  TAG('b', 'd', 'a', 't'),  // 40
+  TAG('b', 'l', 'o', 'c'),  // 41
+  TAG('b', 's', 'l', 'n'),  // 42
+  TAG('c', 'v', 'a', 'r'),  // 43
+  TAG('f', 'd', 's', 'c'),  // 44
+  TAG('f', 'e', 'a', 't'),  // 45
+  TAG('f', 'm', 't', 'x'),  // 46
+  TAG('f', 'v', 'a', 'r'),  // 47
+  TAG('g', 'v', 'a', 'r'),  // 48
+  TAG('h', 's', 't', 'y'),  // 49
+  TAG('j', 'u', 's', 't'),  // 50
+  TAG('l', 'c', 'a', 'r'),  // 51
+  TAG('m', 'o', 'r', 't'),  // 52
+  TAG('m', 'o', 'r', 'x'),  // 53
+  TAG('o', 'p', 'b', 'd'),  // 54
+  TAG('p', 'r', 'o', 'p'),  // 55
+  TAG('t', 'r', 'a', 'k'),  // 56
+  TAG('Z', 'a', 'p', 'f'),  // 57
+  TAG('S', 'i', 'l', 'f'),  // 58
+  TAG('G', 'l', 'a', 't'),  // 59
+  TAG('G', 'l', 'o', 'c'),  // 60
+  TAG('F', 'e', 'a', 't'),  // 61
+  TAG('S', 'i', 'l', 'l'),  // 62
+};
+
+struct Point {
+  int16_t x;
+  int16_t y;
+  bool on_curve;
+};
+
+struct Table {
+  uint32_t tag;
+  uint32_t flags;
+
+  uint32_t transform_length;
+
+  uint32_t dst_offset;
+  uint32_t dst_length;
+
+  Table()
+      : tag(0),
+        flags(0),
+        transform_length(0),
+        dst_offset(0),
+        dst_length(0) {}
+};
+
+// Based on section 6.1.1 of MicroType Express draft spec
+bool Read255UShort(ots::Buffer* buf, uint16_t* value) {
+  static const uint8_t kWordCode = 253;
+  static const uint8_t kOneMoreByteCode2 = 254;
+  static const uint8_t kOneMoreByteCode1 = 255;
+  static const uint8_t kLowestUCode = 253;
+  uint8_t code = 0;
+  if (!buf->ReadU8(&code)) {
+    return OTS_FAILURE();
+  }
+  if (code == kWordCode) {
+    uint16_t result = 0;
+    if (!buf->ReadU16(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result;
+    return true;
+  } else if (code == kOneMoreByteCode1) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode;
+    return true;
+  } else if (code == kOneMoreByteCode2) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode * 2;
+    return true;
+  } else {
+    *value = code;
+    return true;
+  }
+}
+
+bool ReadBase128(ots::Buffer* buf, uint32_t* value) {
+  uint32_t result = 0;
+  for (size_t i = 0; i < 5; ++i) {
+    uint8_t code = 0;
+    if (!buf->ReadU8(&code)) {
+      return OTS_FAILURE();
+    }
+    // If any of the top seven bits are set then we're about to overflow.
+    if (result & 0xfe000000U) {
+      return OTS_FAILURE();
+    }
+    result = (result << 7) | (code & 0x7f);
+    if ((code & 0x80) == 0) {
+      *value = result;
+      return true;
+    }
+  }
+  // Make sure not to exceed the size bound
+  return OTS_FAILURE();
+}
+
+// Caller must ensure that buffer overrun won't happen.
+// TODO(ksakamaoto): Consider creating 'writer' version of the Buffer class
+// and use it across the code.
+size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
+  dst[offset] = x >> 24;
+  dst[offset + 1] = (x >> 16) & 0xff;
+  dst[offset + 2] = (x >> 8) & 0xff;
+  dst[offset + 3] = x & 0xff;
+  return offset + 4;
+}
+
+size_t StoreU16(uint8_t* dst, size_t offset, uint16_t x) {
+  dst[offset] = x >> 8;
+  dst[offset + 1] = x & 0xff;
+  return offset + 2;
+}
+
+int WithSign(int flag, int baseval) {
+  assert(0 <= baseval && baseval < 65536);
+  return (flag & 1) ? baseval : -baseval;
+}
+
+bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
+    unsigned int n_points, std::vector<Point>* result,
+    size_t* in_bytes_consumed) {
+  int x = 0;
+  int y = 0;
+
+  // Early return if |in| buffer is too small. Each point consumes 1-4 bytes.
+  if (n_points > in_size) {
+    return OTS_FAILURE();
+  }
+  unsigned int triplet_index = 0;
+
+  for (unsigned int i = 0; i < n_points; ++i) {
+    uint8_t flag = flags_in[i];
+    bool on_curve = !(flag >> 7);
+    flag &= 0x7f;
+    unsigned int n_data_bytes;
+    if (flag < 84) {
+      n_data_bytes = 1;
+    } else if (flag < 120) {
+      n_data_bytes = 2;
+    } else if (flag < 124) {
+      n_data_bytes = 3;
+    } else {
+      n_data_bytes = 4;
+    }
+    if (triplet_index + n_data_bytes > in_size ||
+        triplet_index + n_data_bytes < triplet_index) {
+      return OTS_FAILURE();
+    }
+    int dx, dy;
+    if (flag < 10) {
+      dx = 0;
+      dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]);
+    } else if (flag < 20) {
+      dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]);
+      dy = 0;
+    } else if (flag < 84) {
+      int b0 = flag - 20;
+      int b1 = in[triplet_index];
+      dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
+      dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
+    } else if (flag < 120) {
+      int b0 = flag - 84;
+      dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]);
+      dy = WithSign(flag >> 1,
+                    1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]);
+    } else if (flag < 124) {
+      int b2 = in[triplet_index + 1];
+      dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4));
+      dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]);
+    } else {
+      dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]);
+      dy = WithSign(flag >> 1,
+          (in[triplet_index + 2] << 8) + in[triplet_index + 3]);
+    }
+    triplet_index += n_data_bytes;
+    // Possible overflow but coordinate values are not security sensitive
+    x += dx;
+    y += dy;
+    result->push_back(Point());
+    Point& back = result->back();
+    back.x = static_cast<int16_t>(x);
+    back.y = static_cast<int16_t>(y);
+    back.on_curve = on_curve;
+  }
+  *in_bytes_consumed = triplet_index;
+  return true;
+}
+
+// This function stores just the point data. On entry, dst points to the
+// beginning of a simple glyph. Returns true on success.
+bool StorePoints(const std::vector<Point>& points,
+    unsigned int n_contours, unsigned int instruction_length,
+    uint8_t* dst, size_t dst_size, size_t* glyph_size) {
+  // I believe that n_contours < 65536, in which case this is safe. However, a
+  // comment and/or an assert would be good.
+  unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
+    instruction_length;
+  uint8_t last_flag = 0xff;
+  uint8_t repeat_count = 0;
+  int last_x = 0;
+  int last_y = 0;
+  unsigned int x_bytes = 0;
+  unsigned int y_bytes = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    const Point& point = points.at(i);
+    uint8_t flag = point.on_curve ? kGlyfOnCurve : 0;
+    int dx = point.x - last_x;
+    int dy = point.y - last_y;
+    if (dx == 0) {
+      flag |= kGlyfThisXIsSame;
+    } else if (dx > -256 && dx < 256) {
+      flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
+      x_bytes += 1;
+    } else {
+      x_bytes += 2;
+    }
+    if (dy == 0) {
+      flag |= kGlyfThisYIsSame;
+    } else if (dy > -256 && dy < 256) {
+      flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
+      y_bytes += 1;
+    } else {
+      y_bytes += 2;
+    }
+
+    if (flag == last_flag && repeat_count != 255) {
+      dst[flag_offset - 1] |= kGlyfRepeat;
+      repeat_count++;
+    } else {
+      if (repeat_count != 0) {
+        if (flag_offset >= dst_size) {
+          return OTS_FAILURE();
+        }
+        dst[flag_offset++] = repeat_count;
+      }
+      if (flag_offset >= dst_size) {
+        return OTS_FAILURE();
+      }
+      dst[flag_offset++] = flag;
+      repeat_count = 0;
+    }
+    last_x = point.x;
+    last_y = point.y;
+    last_flag = flag;
+  }
+
+  if (repeat_count != 0) {
+    if (flag_offset >= dst_size) {
+      return OTS_FAILURE();
+    }
+    dst[flag_offset++] = repeat_count;
+  }
+  unsigned int xy_bytes = x_bytes + y_bytes;
+  if (xy_bytes < x_bytes ||
+      flag_offset + xy_bytes < flag_offset ||
+      flag_offset + xy_bytes > dst_size) {
+    return OTS_FAILURE();
+  }
+
+  int x_offset = flag_offset;
+  int y_offset = flag_offset + x_bytes;
+  last_x = 0;
+  last_y = 0;
+  for (size_t i = 0; i < points.size(); ++i) {
+    int dx = points.at(i).x - last_x;
+    if (dx == 0) {
+      // pass
+    } else if (dx > -256 && dx < 256) {
+      dst[x_offset++] = static_cast<uint8_t>(std::abs(dx));
+    } else {
+      // will always fit for valid input, but overflow is harmless
+      x_offset = StoreU16(dst, x_offset, static_cast<uint16_t>(dx));
+    }
+    last_x += dx;
+    int dy = points.at(i).y - last_y;
+    if (dy == 0) {
+      // pass
+    } else if (dy > -256 && dy < 256) {
+      dst[y_offset++] = static_cast<uint8_t>(std::abs(dy));
+    } else {
+      y_offset = StoreU16(dst, y_offset, static_cast<uint16_t>(dy));
+    }
+    last_y += dy;
+  }
+  *glyph_size = y_offset;
+  return true;
+}
+
+// Compute the bounding box of the coordinates, and store into a glyf buffer.
+// A precondition is that there are at least 10 bytes available.
+void ComputeBbox(const std::vector<Point>& points, uint8_t* dst) {
+  int16_t x_min = 0;
+  int16_t y_min = 0;
+  int16_t x_max = 0;
+  int16_t y_max = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    int16_t x = points.at(i).x;
+    int16_t y = points.at(i).y;
+    if (i == 0 || x < x_min) x_min = x;
+    if (i == 0 || x > x_max) x_max = x;
+    if (i == 0 || y < y_min) y_min = y;
+    if (i == 0 || y > y_max) y_max = y;
+  }
+  size_t offset = 2;
+  offset = StoreU16(dst, offset, x_min);
+  offset = StoreU16(dst, offset, y_min);
+  offset = StoreU16(dst, offset, x_max);
+  offset = StoreU16(dst, offset, y_max);
+}
+
+// Process entire bbox stream. This is done as a separate pass to allow for
+// composite bbox computations (an optional more aggressive transform).
+bool ProcessBboxStream(ots::Buffer* bbox_stream, unsigned int n_glyphs,
+    const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
+    size_t glyf_buf_length) {
+  const uint8_t* buf = bbox_stream->buffer();
+  if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
+    return OTS_FAILURE();
+  }
+  // Safe because n_glyphs is bounded
+  unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
+  if (!bbox_stream->Skip(bitmap_length)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned int i = 0; i < n_glyphs; ++i) {
+    if (buf[i >> 3] & (0x80 >> (i & 7))) {
+      uint32_t loca_offset = loca_values.at(i);
+      if (loca_values.at(i + 1) - loca_offset < kEndPtsOfContoursOffset) {
+        return OTS_FAILURE();
+      }
+      if (glyf_buf_length < 2 + 10 ||
+          loca_offset > glyf_buf_length - 2 - 10) {
+        return OTS_FAILURE();
+      }
+      if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ProcessComposite(ots::Buffer* composite_stream, uint8_t* dst,
+    size_t dst_size, size_t* glyph_size, bool* have_instructions) {
+  size_t start_offset = composite_stream->offset();
+  bool we_have_instructions = false;
+
+  uint16_t flags = FLAG_MORE_COMPONENTS;
+  while (flags & FLAG_MORE_COMPONENTS) {
+    if (!composite_stream->ReadU16(&flags)) {
+      return OTS_FAILURE();
+    }
+    we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0;
+    size_t arg_size = 2;  // glyph index
+    if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) {
+      arg_size += 4;
+    } else {
+      arg_size += 2;
+    }
+    if (flags & FLAG_WE_HAVE_A_SCALE) {
+      arg_size += 2;
+    } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+      arg_size += 4;
+    } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) {
+      arg_size += 8;
+    }
+    if (!composite_stream->Skip(arg_size)) {
+      return OTS_FAILURE();
+    }
+  }
+  size_t composite_glyph_size = composite_stream->offset() - start_offset;
+  if (composite_glyph_size + kCompositeGlyphBegin > dst_size) {
+    return OTS_FAILURE();
+  }
+  StoreU16(dst, 0, 0xffff);  // nContours = -1 for composite glyph
+  std::memcpy(dst + kCompositeGlyphBegin,
+      composite_stream->buffer() + start_offset,
+      composite_glyph_size);
+  *glyph_size = kCompositeGlyphBegin + composite_glyph_size;
+  *have_instructions = we_have_instructions;
+  return true;
+}
+
+// Build TrueType loca table
+bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
+    uint8_t* dst, size_t dst_size) {
+  const uint64_t loca_size = loca_values.size();
+  const uint64_t offset_size = index_format ? 4 : 2;
+  if ((loca_size << 2) >> 2 != loca_size) {
+    return OTS_FAILURE();
+  }
+  // No integer overflow here (loca_size <= 2^16).
+  if (offset_size * loca_size > dst_size) {
+    return OTS_FAILURE();
+  }
+  size_t offset = 0;
+  for (size_t i = 0; i < loca_values.size(); ++i) {
+    uint32_t value = loca_values.at(i);
+    if (index_format) {
+      offset = StoreU32(dst, offset, value);
+    } else {
+      offset = StoreU16(dst, offset, static_cast<uint16_t>(value >> 1));
+    }
+  }
+  return true;
+}
+
+// Reconstruct entire glyf table based on transformed original
+bool ReconstructGlyf(const uint8_t* data, size_t data_size,
+    uint8_t* dst, size_t dst_size,
+    uint8_t* loca_buf, size_t loca_size) {
+  static const int kNumSubStreams = 7;
+  ots::Buffer file(data, data_size);
+  uint32_t version;
+  std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
+
+  if (!file.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  uint16_t num_glyphs;
+  uint16_t index_format;
+  if (!file.ReadU16(&num_glyphs) ||
+      !file.ReadU16(&index_format)) {
+    return OTS_FAILURE();
+  }
+  unsigned int offset = (2 + kNumSubStreams) * 4;
+  if (offset > data_size) {
+    return OTS_FAILURE();
+  }
+  // Invariant from here on: data_size >= offset
+  for (int i = 0; i < kNumSubStreams; ++i) {
+    uint32_t substream_size;
+    if (!file.ReadU32(&substream_size)) {
+      return OTS_FAILURE();
+    }
+    if (substream_size > data_size - offset) {
+      return OTS_FAILURE();
+    }
+    substreams.at(i) = std::make_pair(data + offset, substream_size);
+    offset += substream_size;
+  }
+  ots::Buffer n_contour_stream(substreams.at(0).first, substreams.at(0).second);
+  ots::Buffer n_points_stream(substreams.at(1).first, substreams.at(1).second);
+  ots::Buffer flag_stream(substreams.at(2).first, substreams.at(2).second);
+  ots::Buffer glyph_stream(substreams.at(3).first, substreams.at(3).second);
+  ots::Buffer composite_stream(substreams.at(4).first, substreams.at(4).second);
+  ots::Buffer bbox_stream(substreams.at(5).first, substreams.at(5).second);
+  ots::Buffer instruction_stream(substreams.at(6).first,
+                                 substreams.at(6).second);
+
+  std::vector<uint32_t> loca_values;
+  loca_values.reserve(num_glyphs + 1);
+  std::vector<uint16_t> n_points_vec;
+  std::vector<Point> points;
+  uint32_t loca_offset = 0;
+  for (unsigned int i = 0; i < num_glyphs; ++i) {
+    size_t glyph_size = 0;
+    uint16_t n_contours = 0;
+    if (!n_contour_stream.ReadU16(&n_contours)) {
+      return OTS_FAILURE();
+    }
+    uint8_t* glyf_dst = dst + loca_offset;
+    size_t glyf_dst_size = dst_size - loca_offset;
+    if (n_contours == 0xffff) {
+      // composite glyph
+      bool have_instructions = false;
+      uint16_t instruction_size = 0;
+      if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
+            &glyph_size, &have_instructions)) {
+        return OTS_FAILURE();
+      }
+      if (have_instructions) {
+        if (!Read255UShort(&glyph_stream, &instruction_size)) {
+          return OTS_FAILURE();
+        }
+        // No integer overflow here (instruction_size < 2^16).
+        if (instruction_size + 2U > glyf_dst_size - glyph_size) {
+          return OTS_FAILURE();
+        }
+        StoreU16(glyf_dst, glyph_size, instruction_size);
+        if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
+              instruction_size)) {
+          return OTS_FAILURE();
+        }
+        glyph_size += instruction_size + 2;
+      }
+    } else if (n_contours > 0) {
+      // simple glyph
+      n_points_vec.clear();
+      points.clear();
+      uint32_t total_n_points = 0;
+      uint16_t n_points_contour;
+      for (uint32_t j = 0; j < n_contours; ++j) {
+        if (!Read255UShort(&n_points_stream, &n_points_contour)) {
+          return OTS_FAILURE();
+        }
+        n_points_vec.push_back(n_points_contour);
+        if (total_n_points + n_points_contour < total_n_points) {
+          return OTS_FAILURE();
+        }
+        total_n_points += n_points_contour;
+      }
+      uint32_t flag_size = total_n_points;
+      if (flag_size > flag_stream.length() - flag_stream.offset()) {
+        return OTS_FAILURE();
+      }
+      const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset();
+      const uint8_t* triplet_buf = glyph_stream.buffer() +
+        glyph_stream.offset();
+      size_t triplet_size = glyph_stream.length() - glyph_stream.offset();
+      size_t triplet_bytes_consumed = 0;
+      if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points,
+            &points, &triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      const uint32_t header_and_endpts_contours_size =
+          kEndPtsOfContoursOffset + 2 * n_contours;
+      if (glyf_dst_size < header_and_endpts_contours_size) {
+        return OTS_FAILURE();
+      }
+      StoreU16(glyf_dst, 0, n_contours);
+      ComputeBbox(points, glyf_dst);
+      size_t endpts_offset = kEndPtsOfContoursOffset;
+      int end_point = -1;
+      for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
+        end_point += n_points_vec.at(contour_ix);
+        if (end_point >= 65536) {
+          return OTS_FAILURE();
+        }
+        endpts_offset = StoreU16(glyf_dst, endpts_offset, static_cast<uint16_t>(end_point));
+      }
+      if (!flag_stream.Skip(flag_size)) {
+        return OTS_FAILURE();
+      }
+      if (!glyph_stream.Skip(triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      uint16_t instruction_size;
+      if (!Read255UShort(&glyph_stream, &instruction_size)) {
+        return OTS_FAILURE();
+      }
+      // No integer overflow here (instruction_size < 2^16).
+      if (glyf_dst_size - header_and_endpts_contours_size <
+          instruction_size + 2U) {
+        return OTS_FAILURE();
+      }
+      uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size;
+      StoreU16(instruction_dst, 0, instruction_size);
+      if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) {
+        return OTS_FAILURE();
+      }
+      if (!StorePoints(points, n_contours, instruction_size,
+            glyf_dst, glyf_dst_size, &glyph_size)) {
+        return OTS_FAILURE();
+      }
+    } else {
+      glyph_size = 0;
+    }
+    loca_values.push_back(loca_offset);
+    if (glyph_size + 3 < glyph_size) {
+      return OTS_FAILURE();
+    }
+    glyph_size = ots::Round2(glyph_size);
+    if (glyph_size > dst_size - loca_offset) {
+      // This shouldn't happen, but this test defensively maintains the
+      // invariant that loca_offset <= dst_size.
+      return OTS_FAILURE();
+    }
+    loca_offset += glyph_size;
+  }
+  loca_values.push_back(loca_offset);
+  assert(loca_values.size() == static_cast<size_t>(num_glyphs + 1));
+  if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values,
+          dst, dst_size)) {
+    return OTS_FAILURE();
+  }
+  return StoreLoca(loca_values, index_format, loca_buf, loca_size);
+}
+
+// This is linear search, but could be changed to binary because we
+// do have a guarantee that the tables are sorted by tag. But the total
+// cpu time is expected to be very small in any case.
+const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
+  size_t n_tables = tables.size();
+  for (size_t i = 0; i < n_tables; ++i) {
+    if (tables.at(i).tag == tag) {
+      return &tables.at(i);
+    }
+  }
+  return NULL;
+}
+
+bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
+    const uint8_t* transformed_buf, size_t transformed_size,
+    uint8_t* dst, size_t dst_length) {
+  if (tag == TAG('g', 'l', 'y', 'f')) {
+    const Table* glyf_table = FindTable(tables, tag);
+    const Table* loca_table = FindTable(tables, TAG('l', 'o', 'c', 'a'));
+    if (glyf_table == NULL || loca_table == NULL) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(glyf_table->dst_offset) + glyf_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(loca_table->dst_offset) + loca_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    return ReconstructGlyf(transformed_buf, transformed_size,
+        dst + glyf_table->dst_offset, glyf_table->dst_length,
+        dst + loca_table->dst_offset, loca_table->dst_length);
+  } else if (tag == TAG('l', 'o', 'c', 'a')) {
+    // processing was already done by glyf table, but validate
+    if (!FindTable(tables, TAG('g', 'l', 'y', 'f'))) {
+      return OTS_FAILURE();
+    }
+  } else {
+    // transform for the tag is not known
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
+  uint32_t checksum = 0;
+  for (size_t i = 0; i < size; i += 4) {
+    // We assume the addition is mod 2^32, which is valid because unsigned
+    checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
+      (buf[i + 2] << 8) | buf[i + 3];
+  }
+  return checksum;
+}
+
+bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
+  const Table* head_table = FindTable(tables, TAG('h', 'e', 'a', 'd'));
+  if (head_table == NULL ||
+      head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
+    return OTS_FAILURE();
+  }
+  size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset;
+  if (adjustment_offset < head_table->dst_offset) {
+    return OTS_FAILURE();
+  }
+  StoreU32(dst, adjustment_offset, 0);
+  size_t n_tables = tables.size();
+  uint32_t file_checksum = 0;
+  for (size_t i = 0; i < n_tables; ++i) {
+    const Table* table = &tables.at(i);
+    size_t table_length = table->dst_length;
+    uint8_t* table_data = dst + table->dst_offset;
+    uint32_t checksum = ComputeChecksum(table_data, table_length);
+    StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
+    file_checksum += checksum;  // The addition is mod 2^32
+  }
+  file_checksum += ComputeChecksum(dst,
+      kSfntHeaderSize + kSfntEntrySize * n_tables);
+  uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
+  StoreU32(dst, adjustment_offset, checksum_adjustment);
+  return true;
+}
+
+bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
+    const uint8_t* src_buf, size_t src_size) {
+  size_t uncompressed_size = dst_size;
+  int ok = BrotliDecompressBuffer(src_size, src_buf,
+                                  &uncompressed_size, dst_buf);
+  if (!ok || uncompressed_size != dst_size) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ReadTableDirectory(ots::OpenTypeFile* file,
+    ots::Buffer* buffer, std::vector<Table>* tables,
+    size_t num_tables) {
+  for (size_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables->at(i);
+    uint8_t flag_byte;
+    if (!buffer->ReadU8(&flag_byte)) {
+      return OTS_FAILURE_MSG("Failed to read the flags of table directory entry %d", i);
+    }
+    uint32_t tag;
+    if ((flag_byte & 0x3f) == 0x3f) {
+      if (!buffer->ReadU32(&tag)) {
+        return OTS_FAILURE_MSG("Failed to read the tag of table directory entry %d", i);
+      }
+    } else {
+      tag = kKnownTags[flag_byte & 0x3f];
+    }
+    // Bits 6 and 7 are reserved and must be 0.
+    if ((flag_byte & 0xc0) != 0) {
+      return OTS_FAILURE_MSG("Bits 6 and 7 are not 0 for table directory entry %d", i);
+    }
+    uint32_t flags = 0;
+    // Always transform the glyf and loca tables
+    if (tag == TAG('g', 'l', 'y', 'f') ||
+        tag == TAG('l', 'o', 'c', 'a')) {
+      flags |= kWoff2FlagsTransform;
+    }
+    uint32_t dst_length;
+    if (!ReadBase128(buffer, &dst_length)) {
+      return OTS_FAILURE_MSG("Failed to read \"origLength\" for table %4.4s", (char*)&tag);
+    }
+    uint32_t transform_length = dst_length;
+    if ((flags & kWoff2FlagsTransform) != 0) {
+      if (!ReadBase128(buffer, &transform_length)) {
+        return OTS_FAILURE_MSG("Failed to read \"transformLength\" for table %4.4s", (char*)&tag);
+      }
+    }
+    // Disallow huge numbers (> 1GB) for sanity.
+    if (transform_length > 1024 * 1024 * 1024 ||
+        dst_length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("\"origLength\" or \"transformLength\" > 1GB");
+    }
+    table->tag = tag;
+    table->flags = flags;
+    table->transform_length = transform_length;
+    table->dst_length = dst_length;
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
+  ots::Buffer file(data, length);
+  uint32_t total_length;
+
+  if (!file.Skip(16) ||
+      !file.ReadU32(&total_length)) {
+    return 0;
+  }
+  return total_length;
+}
+
+bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
+                       uint8_t* result, size_t result_length,
+                       const uint8_t* data, size_t length) {
+  static const uint32_t kWoff2Signature = 0x774f4632;  // "wOF2"
+  ots::Buffer buffer(data, length);
+
+  uint32_t signature;
+  uint32_t flavor = 0;
+  if (!buffer.ReadU32(&signature) || signature != kWoff2Signature ||
+      !buffer.ReadU32(&flavor)) {
+    return OTS_FAILURE_MSG("Failed to read \"signature\" or \"flavor\", or not WOFF2 signature");
+  }
+
+  if (!IsValidVersionTag(ntohl(flavor))) {
+    return OTS_FAILURE_MSG("Invalid \"flavor\"");
+  }
+
+  uint32_t reported_length;
+  if (!buffer.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE_MSG("Failed to read \"length\" or it does not match the actual file size");
+  }
+  uint16_t num_tables;
+  if (!buffer.ReadU16(&num_tables) || !num_tables) {
+    return OTS_FAILURE_MSG("Failed to read \"numTables\"");
+  }
+  // We don't care about these fields of the header:
+  //   uint16_t reserved
+  //   uint32_t total_sfnt_size
+  if (!buffer.Skip(6)) {
+    return OTS_FAILURE_MSG("Failed to read \"reserve\" or \"totalSfntSize\"");
+  }
+  uint32_t compressed_length;
+  if (!buffer.ReadU32(&compressed_length)) {
+    return OTS_FAILURE_MSG("Failed to read \"totalCompressedSize\"");
+  }
+  if (compressed_length > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  //   uint32_t meta_offset, meta_length, meta_orig_length
+  //   uint32_t priv_offset, priv_length
+  if (!buffer.Skip(24)) {
+    return OTS_FAILURE();
+  }
+  std::vector<Table> tables(num_tables);
+  if (!ReadTableDirectory(file, &buffer, &tables, num_tables)) {
+    return OTS_FAILURE_MSG("Failed to read table directory");
+  }
+  uint64_t compressed_offset = buffer.offset();
+  if (compressed_offset > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+  uint64_t dst_offset = kSfntHeaderSize +
+      kSfntEntrySize * static_cast<uint64_t>(num_tables);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables.at(i);
+    table->dst_offset = static_cast<uint32_t>(dst_offset);
+    dst_offset += table->dst_length;
+    if (dst_offset > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+    dst_offset = ots::Round4(dst_offset);
+  }
+  if (ots::Round4(compressed_offset + compressed_length) > length || dst_offset > result_length) {
+    return OTS_FAILURE();
+  }
+
+  const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
+  if (sfnt_header_and_table_directory_size > result_length) {
+    return OTS_FAILURE();
+  }
+
+  // Start building the font
+  size_t offset = 0;
+  offset = StoreU32(result, offset, flavor);
+  offset = StoreU16(result, offset, num_tables);
+  uint8_t max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+  offset = StoreU16(result, offset, output_search_range);
+  offset = StoreU16(result, offset, max_pow2);
+  offset = StoreU16(result, offset, (num_tables << 4) - output_search_range);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    offset = StoreU32(result, offset, table->tag);
+    offset = StoreU32(result, offset, 0);  // checksum, to fill in later
+    offset = StoreU32(result, offset, table->dst_offset);
+    offset = StoreU32(result, offset, table->dst_length);
+  }
+  std::vector<uint8_t> uncompressed_buf;
+  const uint8_t* transform_buf = NULL;
+  uint64_t total_size = 0;
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    total_size += tables.at(i).transform_length;
+    if (total_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+  }
+  // Enforce same 30M limit on uncompressed tables as OTS
+  if (total_size > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+  const size_t total_size_size_t = static_cast<size_t>(total_size);
+  uncompressed_buf.resize(total_size_size_t);
+  const uint8_t* src_buf = data + compressed_offset;
+  if (!Woff2Uncompress(&uncompressed_buf[0], total_size_size_t,
+      src_buf, compressed_length)) {
+    return OTS_FAILURE();
+  }
+  transform_buf = &uncompressed_buf[0];
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    uint32_t flags = table->flags;
+    size_t transform_length = table->transform_length;
+
+    if ((flags & kWoff2FlagsTransform) == 0) {
+      if (transform_length != table->dst_length) {
+        return OTS_FAILURE();
+      }
+      if (static_cast<uint64_t>(table->dst_offset) + transform_length >
+          result_length) {
+        return OTS_FAILURE();
+      }
+      std::memcpy(result + table->dst_offset, transform_buf,
+          transform_length);
+    } else {
+      if (!ReconstructTransformed(tables, table->tag,
+            transform_buf, transform_length, result, result_length)) {
+        return OTS_FAILURE();
+      }
+    }
+
+    transform_buf += transform_length;
+    if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return FixChecksums(tables, result);
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/woff2.h b/third_party/ots/src/woff2.h
new file mode 100644
index 0000000..1db259a
--- /dev/null
+++ b/third_party/ots/src/woff2.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_WOFF2_H_
+#define OTS_WOFF2_H_
+
+namespace ots {
+
+// Compute the size of the final uncompressed font, or 0 on error.
+size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
+
+// Decompresses the font into the target buffer. The result_length should
+// be the same as determined by ComputeFinalSize(). Returns true on successful
+// decompression.
+bool ConvertWOFF2ToTTF(OpenTypeFile *file, uint8_t *result, size_t result_length,
+                       const uint8_t *data, size_t length);
+}
+
+#endif  // OTS_WOFF2_H_
diff --git a/third_party/ots/test/BLACKLIST.txt b/third_party/ots/test/BLACKLIST.txt
new file mode 100644
index 0000000..f3cd207
--- /dev/null
+++ b/third_party/ots/test/BLACKLIST.txt
@@ -0,0 +1,124 @@
+# Required table(s) are missing (e.g. OS/2 table).
+AppleGothic.ttf
+AppleMyungjo.ttf
+ArialHB.ttf
+ArialHBBold.ttf
+Corsiva.ttf
+CorsivaBold.ttf
+InaiMathi.ttf
+NISC18030.ttf
+NewPeninimMT.ttf
+NewPeninimMTBold.ttf
+NewPeninimMTBoldInclined.ttf
+NewPeninimMTInclined.ttf
+Raanana.ttf
+RaananaBold.ttf
+
+# The length field of a table is weird.
+homa.ttf
+nazli.ttf
+titr.ttf
+ume-tgc4.ttf
+ume-tgs4.ttf
+ume-tgc5.ttf
+ume-tgs5.ttf
+ume-tms3.ttf
+
+# Table(s) are not 4-byte aligned.
+UnBatang.ttf
+UnBom.ttf
+UnDotum.ttf
+UnGraphic.ttf
+UnGungseo.ttf
+UnJamoBatang.ttf
+UnJamoDotum.ttf
+UnJamoNovel.ttf
+UnJamoSora.ttf
+UnPenheulim.ttf
+UnPen.ttf
+UnPilgiBold.ttf
+UnPilgi.ttf
+UnShinmun.ttf
+UnTaza.ttf
+UnYetgul.ttf
+
+# Tables are not sorted by table tags.
+f500.ttf
+
+# non-ASCII characters are used in a table tag
+SyrCOMAdiabene.otf
+SyrCOMAntioch.otf
+SyrCOMBatnanBold.otf
+SyrCOMBatnan.otf
+SyrCOMCtesiphon.otf
+SyrCOMJerusalemBold.otf
+SyrCOMJerusalemItalic.otf
+SyrCOMJerusalem.otf
+SyrCOMJerusalemOutline.otf
+SyrCOMKharput.otf
+SyrCOMMalankara.otf
+SyrCOMMardinBold.otf
+SyrCOMMardin.otf
+SyrCOMMidyat.otf
+SyrCOMNisibin.otf
+SyrCOMNisibinOutline.otf
+SyrCOMQenNeshrin.otf
+SyrCOMTalada.otf
+SyrCOMTurAbdin.otf
+SyrCOMUrhoyBold.otf
+SyrCOMUrhoy.otf
+
+# Malformed SFNT table; unexpected entry selector
+misakimn.ttf
+misaki.ttf
+
+# Malformed CMAP table; Subtables are not sorted by platform ID
+ani.ttf
+Caliban.ttf
+
+# Malformed CMAP table; Entries in a 3-0-4 or 3-1-4 subtable are not sorted.
+LucidaSansOblique.ttf
+LucidaTypewriterOblique.ttf
+bkai00mp.ttf
+bsmi00lp.ttf
+modelwor.ttf
+
+# Malformed CMAP table; "search range" in a 3-0-4 or 3-1-4 subtable are invalid.
+cmmi10.ttf
+cmsy10.ttf
+msam10.ttf
+
+# Malformed CMAP table; The 3-10-12 table is too short.
+BPG_Chveulebrivi.ttf
+BPG_Chveulebrivi_bold.ttf
+
+# Unsupported CMAP table; ots doesn't support non-Unicode fonts.
+Apple Symbols.ttf
+儷宋 Pro.ttf
+儷黑 Pro.ttf
+华文仿宋.ttf
+华文宋体.ttf
+华文楷体.ttf
+华文细黑.ttf
+华文黑体.ttf
+
+# Unsupported CMAP table; The Unicode BMP table is missing, while the UCS-4 table is available.
+DroidSansJapanese.ttf
+DroidSansFallback.ttf
+
+# Malformed GLYF table; The content of flags array and the lengths of xCoordinates, yCoordinates are inconsistent.
+DecoTypeNaskh.ttf
+
+# Malformed HMTX table; The table is too short.
+mona.ttf
+
+# CMAP glyph id is out of range.
+Samyak-Oriya.ttf
+
+# Unsupported CFF table; "supplemental encoding" is not supported at the moment. This should be fixed in the future.
+Walbf___.otf
+
+# GDEF MarkAttachClassDef offset is invalid.
+ManchuFont.ttf
+arianamu.ttf
+summersby.ttf
diff --git a/third_party/ots/test/README b/third_party/ots/test/README
new file mode 100644
index 0000000..f634e06
--- /dev/null
+++ b/third_party/ots/test/README
@@ -0,0 +1,243 @@
+------------------------------------------------------------------------------
+ot-sanitise - TTF/OTF font validator/transcoder
+
+Description:
+  ot-sanitise is a program which validates and/or transcodes a truetype or
+  opentype font file using the OTS library:
+
+      transcoded_font = ValidateAndTranscode(original_font);
+      if (validation_error)
+        PrintErrorAndExit;
+      OutputToStdout(transcoded_font);
+
+Usage:
+  $ ./ot-sanitise ttf_or_otf_file [transcoded_file]
+
+Example:
+  $ ./ot-sanitise sample.otf transcoded_sample.otf
+  $ ./ot-sanitise malformed.ttf
+  WARNING at ots/src/ots.cc:158: bad range shift
+  ERROR at ots/src/ots.cc:199 (bool<unnamed>::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t))
+  Failed to sanitise file!
+  $
+
+------------------------------------------------------------------------------
+idempotent - TTF/OTF font transcoder (for OTS debugging)
+
+Description:
+  idempotent is a program which validates and transcodes a truetype or opentype
+  font file using OTS. This tool transcodes the original font twice and then
+  verifies that the two transcoded fonts are identical:
+
+      t1 = ValidateAndTranscode(original_font);
+      if (validation_error)
+        PrintErrorAndExit;
+      t2 = ValidateAndTranscode(t1);
+      if (validation_error)
+        PrintErrorAndExit;
+      if (t1 != t2)
+        PrintErrorAndExit;
+
+  This tool is basically for OTS developers.
+
+Usage:
+  $ ./idempotent ttf_or_otf_file
+
+Example:
+  $ ./idempotent sample.otf
+  $ ./idempotent malformed.ttf
+  WARNING at ots/src/ots.cc:158: bad range shift
+  ERROR at ots/src/ots.cc:199 (bool<unnamed>::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t))
+  Failed to sanitise file!
+  $
+
+------------------------------------------------------------------------------
+validator_checker - font validation checker
+
+Description:
+  validator_checker is a program which is intended to validate malformed fonts.
+  If the program detects that the font is invalid, it prints "OK" and returns
+  with 0 (success). If it coulndn't detect any errors, the program then opens
+  the transcoded font and renders some characters using FreeType2:
+
+      transcoded_font = ValidateAndTranscode(malicious_font);
+      if (validation_error)
+        Print("OK");
+      OpenAndRenderSomeCharacters(transcoded_font);  # may cause SIGSEGV
+      Print("OK");
+
+  If SEGV doesn't raise inside FreeType2 library, the program prints "OK" and
+  returns with 0 as well. You should run this tool under the catchsegv or
+  valgrind command so that you can easily verify that all transformed fonts
+  don't crash the library (see the example below).
+
+Usage:
+  $ catchsegv ./validator_checker malicous_ttf_or_otf_file
+
+Example:
+  $ for f in malformed/*.ttf ; do catchsegv ./validator-checker "$f" ; done
+  OK: the malicious font was filtered: malformed/1.ttf
+  OK: the malicious font was filtered: malformed/2.ttf
+  OK: FreeType2 didn't crash: malformed/3.ttf
+  OK: the malicious font was filtered: malformed/4.ttf
+  $
+
+------------------------------------------------------------------------------
+perf - performance checker
+
+Description:
+  perf is a program which validates and transcodes a truetype or opentype font
+  file N times using OTS, then prints the elapsed time:
+
+      for (N times)
+        ValidateAndTranscode(original_font);
+      Print(elapsed_time_in_us / N);
+
+Usage:
+  $ ./perf ttf_or_otf_file
+
+Example:
+  $ ./perf sample.ttf 
+  903 [us] sample.ttf (139332 bytes, 154 [byte/us])
+  $ ./perf sample-bold.otf
+  291 [us] sample-bold.otf (150652 bytes, 517 [byte/us])
+
+------------------------------------------------------------------------------
+side-by-side - font quality checker
+
+Description:
+  side-by-side is a program which renders some characters (ASCII, Latin-1, CJK)
+  using both original font and transcoded font and checks that the two rendering
+  results are exactly equal.
+
+  The following Unicode characters are used during the test:
+    0x0020 - 0x007E  // Basic Latin
+    0x00A1 - 0x017F  // Latin-1
+    0x1100 - 0x11FF  // Hangul
+    0x3040 - 0x309F  // Japanese HIRAGANA letters
+    0x3130 - 0x318F  // Hangul
+    0x4E00 - 0x4F00  // CJK Kanji/Hanja
+    0xAC00 - 0xAD00  // Hangul
+
+  This tool uses FreeType2 library.
+  Note: This tool doesn't check kerning (GPOS/kern) nor font substitution
+  (GSUB). These should be tested in Layout tests if necessary.
+
+Usage:
+  $ ./side-by-side ttf_or_otf_file
+
+Example:
+  $ ./side-by-side linux/kochi-gothic.ttf  # no problem
+  $ ./side-by-side free/kredit1.ttf        # this is known issue of OTS.
+  bitmap metrics doesn't match! (14, 57), (37, 45)
+  EXPECTED:
+                
+    +#######*.  
+   +##########+ 
+  .###+.#.   .#.
+  *#*   #     #*
+  ##.   #     ##
+  ##    #     ##
+  ##    #     ##
+  ##    #.    ##
+  ##.   #.   .##
+  ##.   #.   .##
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   *#*
+  *#+   ++   *#+
+  +#*   +*   *#+
+  +#*   +*   *#+
+  +#*   +*   *#+
+  +#*   +*   ##.
+  +#*   +*   ##.
+  .##   .#   ## 
+  .##   .#   ## 
+  .##   .#   ## 
+   ##    #   ## 
+   ##    #   ## 
+   ##    #  .## 
+   ##    #  .## 
+   ##   .#+ +#* 
+   ##  +######* 
+   ##.+#######* 
+   *##########* 
+   +##########+ 
+    #########*  
+    .########   
+      +####+    
+                
+                
+                
+                
+                
+                
+    .*######*   
+   +##*.*#####  
+  .##+.#+    +# 
+  *#* ##      #+
+  ##*###      ##
+  ######      ##
+  ##+.##+    +##
+  ##  ##########
+  ##  +#########
+  ##   +########
+  *#. .########*
+  .#* #########.
+   +##########+ 
+    +*######*   
+  
+  ACTUAL:
+
+    .*##*+                             
+   +##+.##*.                           
+  .#* .##.+#*                          
+  *#  ###   *#+                        
+  #*######+  .*#+                      
+  #########*.  +#*.                    
+  ###########*   +#*                   
+  *############+   *#+                 
+  +##############.  .##.               
+   *##############*   +#*              
+    +###############+   *#+            
+      *###############+  .*#+          
+       .###############*.  +#*.        
+         +###############*   +#*       
+           *###############+   *#+     
+            .*###############+  .*#+   
+              +###############*.  +#*  
+                +###############*   ** 
+                  *###############+  #+
+                   .###############* ##
+                     +############+  ##
+                       +########*   .##
+                        .######.   +###
+                       +#####+   .*#..#
+                     +#####*    *###..#
+                    *#####.   +#######*
+                  +#####+   .*########.
+                +#####*    +#########* 
+               *#####.   +##########+  
+             +#####+    *#########*.   
+           .#####*    +##########+     
+          *#####.   +##########*       
+        +#####+    *#########*.        
+      .#####*    +##########+          
+     *#####+   +##########*            
+   .#*++#+    *#########*.             
+  .#+  ##   +##########+               
+  ****###+.##########*                 
+  ##################.                  
+  ###+  *#########+                    
+  ##   +########*                      
+  *#+ *########.                       
+   ##.#######+                         
+   +#######*                           
+     *###*.                            
+  
+  
+  Glyph mismatch! (file: free/kredit1.ttf, U+0021, 100pt)!
+  $
+------------------------------------------------------------------------------
diff --git a/third_party/ots/test/cff_type2_charstring_test.cc b/third_party/ots/test/cff_type2_charstring_test.cc
new file mode 100644
index 0000000..21139aa
--- /dev/null
+++ b/third_party/ots/test/cff_type2_charstring_test.cc
@@ -0,0 +1,1584 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff_type2_charstring.h"
+
+#include <gtest/gtest.h>
+
+#include <climits>
+#include <vector>
+
+#include "cff.h"
+
+// Returns a biased number for callsubr and callgsubr operators.
+#define GET_SUBR_NUMBER(n) ((n) - 107)
+#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
+
+namespace {
+
+// A constant which is used in AddSubr function below.
+const int kOpPrefix = INT_MAX;
+
+// Encodes an operator |op| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds.
+bool EncodeOperator(int op, std::vector<uint8_t> *out_bytes) {
+  if (op < 0) {
+    return false;
+  }
+  if (op <= 11) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 12) {
+    return false;
+  }
+  if (op <= 27) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 28) {
+    return false;
+  }
+  if (op <= 31) {
+    out_bytes->push_back(op);
+    return true;
+  }
+
+  const uint8_t upper = (op & 0xff00u) >> 8;
+  const uint8_t lower = op & 0xffu;
+  if (upper != 12) {
+    return false;
+  }
+  out_bytes->push_back(upper);
+  out_bytes->push_back(lower);
+  return true;
+}
+
+// Encodes a number |num| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds. The function does not support 16.16
+// Fixed number.
+bool EncodeNumber(int num, std::vector<uint8_t> *out_bytes) {
+  if (num >= -107 && num <= 107) {
+    out_bytes->push_back(num + 139);
+    return true;
+  }
+  if (num >= 108 && num <= 1131) {
+    const uint8_t v = ((num - 108) / 256) + 247;
+    const uint8_t w = (num - 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  if (num <= -108 && num >= -1131) {
+    const uint8_t v = (-(num + 108) / 256) + 251;
+    const uint8_t w = -(num + 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  if (num <= -32768 && num >= -32767) {
+    const uint8_t v = (num % 0xff00u) >> 8;
+    const uint8_t w = num % 0xffu;
+    out_bytes->push_back(28);
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  return false;
+}
+
+// Adds a subroutine |subr| to |out_buffer| and |out_subr|. The contents of the
+// subroutine is copied to |out_buffer|, and then the position of the subroutine
+// in |out_buffer| is written to |out_subr|. Returns true on success.
+bool AddSubr(const int *subr, size_t subr_len,
+             std::vector<uint8_t>* out_buffer, ots::CFFIndex *out_subr) {
+  size_t pre_offset = out_buffer->size();
+  for (size_t i = 0; i < subr_len; ++i) {
+    if (subr[i] != kOpPrefix) {
+      if (!EncodeNumber(subr[i], out_buffer)) {
+        return false;
+      }
+    } else {
+      if (i + 1 == subr_len) {
+        return false;
+      }
+      ++i;
+      if (!EncodeOperator(subr[i], out_buffer)) {
+        return false;
+      }
+    }
+  }
+
+  ++(out_subr->count);
+  out_subr->off_size = 1;
+  if (out_subr->offsets.empty()) {
+    out_subr->offsets.push_back(pre_offset);
+  }
+  out_subr->offsets.push_back(out_buffer->size());
+  return true;
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool Validate(const int *char_string, size_t char_string_len,
+              const int *global_subrs, size_t global_subrs_len,
+              const int *local_subrs, size_t local_subrs_len) {
+  std::vector<uint8_t> buffer;
+  ots::CFFIndex char_strings_index;
+  ots::CFFIndex global_subrs_index;
+  ots::CFFIndex local_subrs_index;
+
+  if (char_string) {
+    if (!AddSubr(char_string, char_string_len,
+                 &buffer, &char_strings_index)) {
+      return false;
+    }
+  }
+  if (global_subrs) {
+    if (!AddSubr(global_subrs, global_subrs_len,
+                 &buffer, &global_subrs_index)) {
+      return false;
+    }
+  }
+  if (local_subrs) {
+    if (!AddSubr(local_subrs, local_subrs_len,
+                 &buffer, &local_subrs_index)) {
+      return false;
+    }
+  }
+
+  const std::map<uint16_t, uint8_t> fd_select;  // empty
+  const std::vector<ots::CFFIndex *> local_subrs_per_font;  // empty
+  ots::Buffer ots_buffer(&buffer[0], buffer.size());
+
+  ots::OpenTypeFile* file = new ots::OpenTypeFile();
+  file->context = new ots::OTSContext();
+  return ots::ValidateType2CharStringIndex(file,
+                                           char_strings_index,
+                                           global_subrs_index,
+                                           fd_select,
+                                           local_subrs_per_font,
+                                           &local_subrs_index,
+                                           &ots_buffer);
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool ValidateCharStrings(const int *char_string, size_t char_string_len) {
+  return Validate(char_string, char_string_len, NULL, 0, NULL, 0);
+}
+
+}  // namespace
+
+TEST(ValidateTest, TestRMoveTo) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRMoveTo,  // invalid number of args
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      1, 2, kOpPrefix, ots::kHMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kVMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHLineTo,
+      1, 2, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVLineTo,
+      1, 2, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRRCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kRRCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kRRCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kHVCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kHVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRCurveLine) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+      kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRCurveLine,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineCurve) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRLineCurve,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kVHCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kVHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEndChar) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestHStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHintMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kHintMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kHintMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kHintMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kHintMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCntrMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kCntrMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kCntrMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kCntrMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kCntrMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAbs) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kAbs,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kAbs,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAdd) {
+  {
+    const int char_string[] = {
+      0, 1, kOpPrefix, ots::kAdd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAdd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSub) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kSub,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kSub,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDiv) {
+  // TODO(yusukes): Test div-by-zero.
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kDiv,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kDiv,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNeg) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kNeg,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNeg,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRandom) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kRandom,  // OTS rejects the operator.
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestMul) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kMul,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kMul,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSqrt) {
+  // TODO(yusukes): Test negative numbers.
+  {
+    const int char_string[] = {
+      4, kOpPrefix, ots::kSqrt,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kSqrt,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDrop) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDrop,
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDrop,  // invalid
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestExch) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kExch,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kExch,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIndex) {
+  {
+    const int char_string[] = {
+      1, 2, 3, -1, kOpPrefix, ots::kIndex,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRoll) {
+  {
+    const int char_string[] = {
+      1, 2, 2, 1, kOpPrefix, ots::kRoll,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDup) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDup,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestPut) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kPut,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestGet) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kGet,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAnd) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kAnd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAnd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestOr) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kOr,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kOr,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNot) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kNot,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNot,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEq) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kEq,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kEq,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIfElse) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kIfElse,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, kOpPrefix, ots::kIfElse,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubrWithComputedValues) {
+  {
+    // OTS does not allow to call(g)subr with a subroutine number which is
+    // not a immediate value for safety.
+    const int char_string[] = {
+      0, 0, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestInfiniteLoop) {
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  // mutual recursion which doesn't stop.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+}
+
+TEST(ValidateTest, TestStackOverflow) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9,  // overflow
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDeprecatedOperators) {
+  {
+    const int char_string[] = {
+      kOpPrefix, 16,  // 'blend'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 8,  // 'store'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 13,  // 'load'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestUnterminatedCharString) {
+  // No endchar operator.
+  {
+    const int char_string[] = {
+      123,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456, kOpPrefix, ots::kReturn,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
diff --git a/third_party/ots/test/file-stream.h b/third_party/ots/test/file-stream.h
new file mode 100644
index 0000000..44dd4a1
--- /dev/null
+++ b/third_party/ots/test/file-stream.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_FILE_STREAM_H_
+#define OTS_FILE_STREAM_H_
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+// An OTSStream implementation for testing.
+class FILEStream : public OTSStream {
+ public:
+  explicit FILEStream(FILE *stream)
+      : file_(stream), position_(0) {
+  }
+
+  ~FILEStream() {
+    if (file_)
+      fclose(file_);
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if (!file_ || ::fwrite(data, length, 1, file_) == 1) {
+      position_ += length;
+      return true;
+    }
+    return false;
+  }
+
+  bool Seek(off_t position) {
+#if defined(_WIN32)
+    if (!file_ || !::_fseeki64(file_, position, SEEK_SET)) {
+      position_ = position;
+      return true;
+    }
+#else
+    if (!file_ || !::fseeko(file_, position, SEEK_SET)) {
+      position_ = position;
+      return true;
+    }
+#endif  // defined(_WIN32)
+    return false;
+  }
+
+  off_t Tell() const {
+    return position_;
+  }
+
+ private:
+  FILE * const file_;
+  off_t position_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FILE_STREAM_H_
diff --git a/third_party/ots/test/idempotent.cc b/third_party/ots/test/idempotent.cc
new file mode 100644
index 0000000..ec50ab4
--- /dev/null
+++ b/third_party/ots/test/idempotent.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(_WIN32)
+#ifdef __linux__
+// Linux
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#else
+// Mac OS X
+#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
+#endif  // __linux__
+#include <unistd.h>
+#else
+// Windows
+#include <io.h>
+#include <Windows.h>
+#endif  // !defiend(_WIN32)
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
+  return 1;
+}
+
+bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size);
+bool DumpResults(const uint8_t *result1, const size_t len1,
+                 const uint8_t *result2, const size_t len2);
+
+#if defined(_WIN32)
+#define ADDITIONAL_OPEN_FLAGS O_BINARY
+#else
+#define ADDITIONAL_OPEN_FLAGS 0
+#endif
+
+bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size) {
+  const int fd = open(file_name, O_RDONLY | ADDITIONAL_OPEN_FLAGS);
+  if (fd < 0) {
+    return false;
+  }
+
+  struct stat st;
+  fstat(fd, &st);
+
+  *file_size = st.st_size;
+  *data = new uint8_t[st.st_size];
+  if (read(fd, *data, st.st_size) != st.st_size) {
+    close(fd);
+    return false;
+  }
+  close(fd);
+  return true;
+}
+
+bool DumpResults(const uint8_t *result1, const size_t len1,
+                 const uint8_t *result2, const size_t len2) {
+  int fd1 = open("out1.ttf",
+                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
+  int fd2 = open("out2.ttf",
+                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
+  if (fd1 < 0 || fd2 < 0) {
+    perror("opening output file");
+    return false;
+  }
+  if ((write(fd1, result1, len1) < 0) ||
+      (write(fd2, result2, len2) < 0)) {
+    perror("writing output file");
+    close(fd1);
+    close(fd2);
+    return false;
+  }
+  close(fd1);
+  close(fd2);
+  return true;
+}
+
+// Platform specific implementations.
+bool VerifyTranscodedFont(uint8_t *result, const size_t len);
+
+#if defined(__linux__)
+// Linux
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  FT_Library library;
+  FT_Error error = ::FT_Init_FreeType(&library);
+  if (error) {
+    return false;
+  }
+  FT_Face dummy;
+  error = ::FT_New_Memory_Face(library, result, len, 0, &dummy);
+  if (error) {
+    return false;
+  }
+  ::FT_Done_Face(dummy);
+  return true;
+}
+
+#elif defined(__APPLE_CC__)
+// Mac
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  CFDataRef data = CFDataCreate(0, result, len);
+  if (!data) {
+    return false;
+  }
+
+  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
+  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
+  CGDataProviderRelease(dataProvider);
+  CFRelease(data);
+  if (!cgFontRef) {
+    return false;
+  }
+
+  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
+  CGFontRelease(cgFontRef);
+  if (!numGlyphs) {
+    return false;
+  }
+  return true;
+}
+
+#elif defined(_WIN32)
+// Windows
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  DWORD num_fonts = 0;
+  HANDLE handle = AddFontMemResourceEx(result, len, 0, &num_fonts);
+  if (!handle) {
+    return false;
+  }
+  RemoveFontMemResourceEx(handle);
+  return true;
+}
+
+#else
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  std::fprintf(stderr, "Can't verify the transcoded font on this platform.\n");
+  return false;
+}
+
+#endif
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+
+  size_t file_size = 0;
+  uint8_t *data = 0;
+  if (!ReadFile(argv[1], &data, &file_size)) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+
+  // A transcoded font is usually smaller than an original font.
+  // However, it can be slightly bigger than the original one due to
+  // name table replacement and/or padding for glyf table.
+  //
+  // However, a WOFF font gets decompressed and so can be *much* larger than
+  // the original.
+  uint8_t *result = new uint8_t[file_size * 8];
+  ots::MemoryStream output(result, file_size * 8);
+
+  ots::OTSContext context;
+
+  bool r = context.Process(&output, data, file_size);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+    return 1;
+  }
+  const size_t result_len = output.Tell();
+  delete[] data;
+
+  uint8_t *result2 = new uint8_t[result_len];
+  ots::MemoryStream output2(result2, result_len);
+  r = context.Process(&output2, result, result_len);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise previous output!\n");
+    return 1;
+  }
+  const size_t result2_len = output2.Tell();
+
+  bool dump_results = false;
+  if (result2_len != result_len) {
+    std::fprintf(stderr, "Outputs differ in length\n");
+    dump_results = true;
+  } else if (std::memcmp(result2, result, result_len)) {
+    std::fprintf(stderr, "Outputs differ in content\n");
+    dump_results = true;
+  }
+
+  if (dump_results) {
+    std::fprintf(stderr, "Dumping results to out1.tff and out2.tff\n");
+    if (!DumpResults(result, result_len, result2, result2_len)) {
+      std::fprintf(stderr, "Failed to dump output files.\n");
+      return 1;
+    }
+  }
+
+  // Verify that the transcoded font can be opened by the font renderer for
+  // Linux (FreeType2), Mac OS X, or Windows.
+  if (!VerifyTranscodedFont(result, result_len)) {
+    std::fprintf(stderr, "Failed to verify the transcoded font\n");
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/third_party/ots/test/layout_common_table_test.cc b/third_party/ots/test/layout_common_table_test.cc
new file mode 100644
index 0000000..5e9a03b
--- /dev/null
+++ b/third_party/ots/test/layout_common_table_test.cc
@@ -0,0 +1,761 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "layout.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+const uint32_t kFakeTag = 0x00000000;
+const size_t kScriptRecordSize = 6;
+const size_t kLangSysRecordSize = 6;
+
+bool BuildFakeScriptListTable(ots::OTSStream *out, const uint16_t script_count,
+                              const uint16_t langsys_count,
+                              const uint16_t feature_count) {
+  if (!out->WriteU16(script_count)) {
+    return false;
+  }
+  const off_t script_record_end = out->Tell() +
+      kScriptRecordSize * script_count;
+  const size_t script_table_size = 4 + kLangSysRecordSize * langsys_count;
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!out->WriteU32(kFakeTag) ||
+        !out->WriteU16(script_record_end + i * script_table_size)) {
+      return false;
+    }
+  }
+
+  // Offsets to LangSys tables are measured from the beginning of each
+  // script table.
+  const off_t langsys_record_end = 4 + kLangSysRecordSize * langsys_count;
+  const size_t langsys_table_size = 6 + 2 * feature_count;
+  // Write Fake Script tables.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(langsys_count)) {
+      return false;
+    }
+    for (unsigned j = 0; j < langsys_count; ++j) {
+      if (!out->WriteU32(kFakeTag) ||
+          !out->WriteU16(langsys_record_end + j * langsys_table_size)) {
+        return false;
+      }
+    }
+  }
+
+  // Write Fake LangSys tables.
+  for (unsigned i = 0; i < langsys_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(0xFFFF) ||
+        !out->WriteU16(feature_count)) {
+      return false;
+    }
+    for (unsigned j = 0; j < feature_count; ++j) {
+      if (!out->WriteU16(j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+const size_t kFeatureRecordSize = 6;
+
+bool BuildFakeFeatureListTable(ots::OTSStream *out,
+                               const uint16_t feature_count,
+                               const uint16_t lookup_count) {
+  if (!out->WriteU16(feature_count)) {
+    return false;
+  }
+  const off_t feature_record_end = out->Tell() +
+      kFeatureRecordSize * feature_count;
+  const size_t feature_table_size = 4 + 2 * lookup_count;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!out->WriteU32(kFakeTag) ||
+        !out->WriteU16(feature_record_end + i * feature_table_size)) {
+      return false;
+    }
+  }
+
+  // Write FeatureTable
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(lookup_count)) {
+      return false;
+    }
+    for (uint16_t j = 0; j < lookup_count; ++j) {
+      if (!out->WriteU16(j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BuildFakeLookupListTable(ots::OTSStream *out, const uint16_t lookup_count,
+                              const uint16_t subtable_count) {
+  if (!out->WriteU16(lookup_count)) {
+    return false;
+  }
+  const off_t base_offset_lookup = out->Tell();
+  if (!out->Pad(2 * lookup_count)) {
+    return false;
+  }
+
+  std::vector<off_t> offsets_lookup(lookup_count, 0);
+  for (uint16_t i = 0; i < lookup_count; ++i) {
+    offsets_lookup[i] = out->Tell();
+    if (!out->WriteU16(i + 1) ||
+        !out->WriteU16(0) ||
+        !out->WriteU16(subtable_count) ||
+        !out->Pad(2 * subtable_count) ||
+        !out->WriteU16(0)) {
+      return false;
+    }
+  }
+
+  const off_t offset_lookup_table_end = out->Tell();
+  // Allocate 256 bytes for each subtable.
+  if (!out->Pad(256 * lookup_count * subtable_count)) {
+    return false;
+  }
+
+  if (!out->Seek(base_offset_lookup)) {
+    return false;
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!out->WriteU16(offsets_lookup[i])) {
+      return false;
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!out->Seek(offsets_lookup[i] + 6)) {
+      return false;
+    }
+    for (unsigned j = 0; j < subtable_count; ++j) {
+      if (!out->WriteU16(offset_lookup_table_end +
+                         256*i*subtable_count + 256*j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BuildFakeCoverageFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+  if (!out->WriteU16(1) || !out->WriteU16(glyph_count)) {
+    return false;
+  }
+  for (uint16_t glyph_id = 1; glyph_id <= glyph_count; ++glyph_id) {
+    if (!out->WriteU16(glyph_id)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool BuildFakeCoverageFormat2(ots::OTSStream *out, const uint16_t range_count) {
+  if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+    return false;
+  }
+  uint16_t glyph_id = 1;
+  uint16_t start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    // Write consecutive ranges in which each range consists of two glyph id.
+    if (!out->WriteU16(glyph_id) ||
+        !out->WriteU16(glyph_id + 1) ||
+        !out->WriteU16(start_coverage_index)) {
+      return false;
+    }
+    glyph_id += 2;
+    start_coverage_index += 2;
+  }
+  return true;
+}
+
+bool BuildFakeClassDefFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+  if (!out->WriteU16(1) ||
+      !out->WriteU16(1) ||
+      !out->WriteU16(glyph_count)) {
+    return false;
+  }
+  for (uint16_t class_value = 1; class_value <= glyph_count; ++class_value) {
+    if (!out->WriteU16(class_value)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool BuildFakeClassDefFormat2(ots::OTSStream *out, const uint16_t range_count) {
+  if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+    return false;
+  }
+  uint16_t glyph_id = 1;
+  for (uint16_t class_value = 1; class_value <= range_count; ++class_value) {
+    // Write consecutive ranges in which each range consists of one glyph id.
+    if (!out->WriteU16(glyph_id) ||
+        !out->WriteU16(glyph_id + 1) ||
+        !out->WriteU16(class_value)) {
+      return false;
+    }
+    glyph_id += 2;
+  }
+  return true;
+}
+
+bool BuildFakeDeviceTable(ots::OTSStream *out, const uint16_t start_size,
+                          const uint16_t end_size, const uint16_t format) {
+  if (!out->WriteU16(start_size) ||
+      !out->WriteU16(end_size) ||
+      !out->WriteU16(format)) {
+    return false;
+  }
+
+  const unsigned num_values = std::abs(end_size - start_size) + 1;
+  const unsigned num_bits = (1 << format) * num_values;
+  const unsigned num_units = (num_bits - 1) / 16 + 1;
+  if (!out->Pad(num_units * 2)) {
+    return false;
+  }
+  return true;
+}
+
+class TestStream : public ots::MemoryStream {
+ public:
+  TestStream()
+      : ots::MemoryStream(data_, sizeof(data_)), size_(0) {
+    std::memset(reinterpret_cast<char*>(data_), 0, sizeof(data_));
+  }
+
+  uint8_t* data() { return data_; }
+  size_t size() const { return size_; }
+
+  virtual bool WriteRaw(const void *d, size_t length) {
+    if (Tell() + length > size_) {
+      size_ = Tell() + length;
+    }
+    return ots::MemoryStream::WriteRaw(d, length);
+  }
+
+ private:
+  size_t size_;
+  uint8_t data_[4096];
+};
+
+class TableTest : public ::testing::Test {
+ protected:
+
+  virtual void SetUp() {
+    file = new ots::OpenTypeFile();
+    file->context = new ots::OTSContext();
+  }
+
+  TestStream out;
+  ots::OpenTypeFile *file;
+};
+
+class ScriptListTableTest : public TableTest { };
+class DeviceTableTest : public TableTest { };
+class CoverageTableTest : public TableTest { };
+class CoverageFormat1Test : public TableTest { };
+class CoverageFormat2Test : public TableTest { };
+class ClassDefTableTest : public TableTest { };
+class ClassDefFormat1Test : public TableTest { };
+class ClassDefFormat2Test : public TableTest { };
+class LookupSubtableParserTest : public TableTest { };
+
+class FeatureListTableTest : public TableTest {
+ protected:
+
+  virtual void SetUp() {
+    num_features = 0;
+  }
+
+  uint16_t num_features;
+};
+
+bool fakeTypeParserReturnsTrue(const ots::OpenTypeFile*, const uint8_t *,
+                               const size_t) {
+  return true;
+}
+
+bool fakeTypeParserReturnsFalse(const ots::OpenTypeFile*, const uint8_t *,
+                                const size_t) {
+  return false;
+}
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnTrue[] = {
+  {1, fakeTypeParserReturnsTrue},
+  {2, fakeTypeParserReturnsTrue},
+  {3, fakeTypeParserReturnsTrue},
+  {4, fakeTypeParserReturnsTrue},
+  {5, fakeTypeParserReturnsTrue}
+};
+
+// Fake lookup subtable parser which always returns true.
+const ots::LookupSubtableParser FakeLookupParserReturnsTrue = {
+  5, 5, TypeParsersReturnTrue,
+};
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnFalse[] = {
+  {1, fakeTypeParserReturnsFalse}
+};
+
+// Fake lookup subtable parser which always returns false.
+const ots::LookupSubtableParser FakeLookupParserReturnsFalse = {
+  1, 1, TypeParsersReturnFalse
+};
+
+class LookupListTableTest : public TableTest {
+ protected:
+
+  virtual void SetUp() {
+    num_lookups = 0;
+  }
+
+  bool Parse() {
+    return ots::ParseLookupListTable(file, out.data(), out.size(),
+                                     &FakeLookupParserReturnsTrue,
+                                     &num_lookups);
+  }
+
+  uint16_t num_lookups;
+};
+
+}  // namespace
+
+TEST_F(ScriptListTableTest, TestSuccess) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  EXPECT_TRUE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadScriptCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large script count.
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetUnderflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to ScriptRecord[0].
+  out.Seek(6);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetOverflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to ScriptRecord[0].
+  out.Seek(6);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadLangSysCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large langsys count.
+  out.Seek(10);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetUnderflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to LangSysRecord[0].
+  out.Seek(16);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetOverflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to LangSysRecord[0].
+  out.Seek(16);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadReqFeatureIndex) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature index to ReqFeatureIndex of LangSysTable[0].
+  out.Seek(20);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature count to LangSysTable[0].
+  out.Seek(22);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureIndex) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature index to ReatureIndex[0] of LangSysTable[0].
+  out.Seek(24);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(FeatureListTableTest, TestSuccess) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  EXPECT_TRUE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                         &num_features));
+  EXPECT_EQ(num_features, 1);
+}
+
+TEST_F(FeatureListTableTest, TestSuccess2) {
+  BuildFakeFeatureListTable(&out, 5, 1);
+  EXPECT_TRUE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                         &num_features));
+  EXPECT_EQ(num_features, 5);
+}
+
+TEST_F(FeatureListTableTest, TestBadFeatureCount) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set too large feature count.
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureUnderflow) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set bad offset to FeatureRecord[0].
+  out.Seek(6);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureOverflow) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set bad offset to FeatureRecord[0].
+  out.Seek(6);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestBadLookupCount) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set too large lookup count to FeatureTable[0].
+  out.Seek(10);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(LookupListTableTest, TestSuccess) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  EXPECT_TRUE(Parse());
+  EXPECT_EQ(num_lookups, 1);
+}
+
+TEST_F(LookupListTableTest, TestSuccess2) {
+  BuildFakeLookupListTable(&out, 5, 1);
+  EXPECT_TRUE(Parse());
+  EXPECT_EQ(num_lookups, 5);
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableUnderflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to Lookup[0].
+  out.Seek(2);
+  out.WriteU16(0);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableOverflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to Lookup[0].
+  out.Seek(2);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableUnderflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to SubTable[0] of LookupTable[0].
+  out.Seek(10);
+  out.WriteU16(0);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableOverflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to SubTable[0] of LookupTable[0].
+  out.Seek(10);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupCount) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large lookup count of LookupTable[0].
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupType) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large lookup type of LookupTable[0].
+  out.Seek(4);
+  out.WriteU16(6);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupFlag) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
+  out.Seek(6);
+  out.WriteU16(0x0002);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadSubtableCount) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large sutable count of LookupTable[0].
+  out.Seek(8);
+  out.WriteU16(2);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat1) {
+  BuildFakeCoverageFormat1(&out, 1);
+  EXPECT_TRUE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat2) {
+  BuildFakeCoverageFormat2(&out, 1);
+  EXPECT_TRUE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestBadFormat) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set bad format.
+  out.Seek(0);
+  out.WriteU16(3);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphCount) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set too large glyph count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphId) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set too large glyph id.
+  out.Seek(4);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRangeCount) {
+  BuildFakeCoverageFormat2(&out, 1);
+  // Set too large range count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRange) {
+  BuildFakeCoverageFormat2(&out, 1);
+  // Set reverse order glyph id to start/end fields.
+  out.Seek(4);
+  out.WriteU16(2);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap) {
+  BuildFakeCoverageFormat2(&out, 2);
+  // Set overlapping glyph id to an end field.
+  out.Seek(12);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 2));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap2) {
+  BuildFakeCoverageFormat2(&out, 2);
+  // Set overlapping range.
+  out.Seek(10);
+  out.WriteU16(1);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 2));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat1) {
+  BuildFakeClassDefFormat1(&out, 1);
+  EXPECT_TRUE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat2) {
+  BuildFakeClassDefFormat2(&out, 1);
+  EXPECT_TRUE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestBadFormat) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set bad format.
+  out.Seek(0);
+  out.WriteU16(3);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadStartGlyph) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large start glyph id.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadGlyphCount) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large glyph count.
+  out.Seek(4);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadClassValue) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large class value.
+  out.Seek(6);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestBadRangeCount) {
+  BuildFakeClassDefFormat2(&out, 1);
+  // Set too large range count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap) {
+  BuildFakeClassDefFormat2(&out, 2);
+  // Set overlapping glyph id to an end field.
+  out.Seek(12);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap2) {
+  BuildFakeClassDefFormat2(&out, 2);
+  // Set overlapping range.
+  out.Seek(10);
+  out.WriteU16(1);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success) {
+  BuildFakeDeviceTable(&out, 1, 8, 1);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success2) {
+  BuildFakeDeviceTable(&out, 1, 9, 1);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 1);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 1);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success) {
+  BuildFakeDeviceTable(&out, 1, 1, 2);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success2) {
+  BuildFakeDeviceTable(&out, 1, 8, 2);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 2);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 2);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success) {
+  BuildFakeDeviceTable(&out, 1, 1, 3);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success2) {
+  BuildFakeDeviceTable(&out, 1, 8, 3);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 3);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 3);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(LookupSubtableParserTest, TestSuccess) {
+  {
+    EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 1));
+  }
+  {
+    EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 5));
+  }
+}
+
+TEST_F(LookupSubtableParserTest, TestFail) {
+  {
+    // Pass bad lookup type which less than the smallest type.
+    EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 0));
+  }
+  {
+    // Pass bad lookup type which greater than the maximum type.
+    EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 6));
+  }
+  {
+    // Check the type parser failure.
+    EXPECT_FALSE(FakeLookupParserReturnsFalse.Parse(file, 0, 0, 1));
+  }
+}
diff --git a/third_party/ots/test/ot-sanitise.cc b/third_party/ots/test/ot-sanitise.cc
new file mode 100644
index 0000000..2d4526a
--- /dev/null
+++ b/third_party/ots/test/ot-sanitise.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A very simple driver program while sanitises the file given as argv[1] and
+// writes the sanitised version to stdout.
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(_WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif  // defined(_WIN32)
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#include "file-stream.h"
+#include "opentype-sanitiser.h"
+
+#if defined(_WIN32)
+#define ADDITIONAL_OPEN_FLAGS O_BINARY
+#else
+#define ADDITIONAL_OPEN_FLAGS 0
+#endif
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s ttf_file [dest_ttf_file]\n", argv0);
+  return 1;
+}
+
+class Context: public ots::OTSContext {
+ public:
+  virtual void Message(int level, const char *format, ...) {
+    va_list va;
+
+    if (level == 0)
+      std::fprintf(stderr, "ERROR: ");
+    else
+      std::fprintf(stderr, "WARNING: ");
+    va_start(va, format);
+    std::vfprintf(stderr, format, va);
+    std::fprintf(stderr, "\n");
+    va_end(va);
+  }
+
+  virtual ots::TableAction GetTableAction(uint32_t tag) {
+#define TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+    switch (tag) {
+      case TAG('S','i','l','f'):
+      case TAG('S','i','l','l'):
+      case TAG('G','l','o','c'):
+      case TAG('G','l','a','t'):
+      case TAG('F','e','a','t'):
+        return ots::TABLE_ACTION_PASSTHRU;
+      default:
+        return ots::TABLE_ACTION_DEFAULT;
+    }
+#undef TAG
+  }
+};
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc < 2 || argc > 3) return Usage(argv[0]);
+
+  const int fd = ::open(argv[1], O_RDONLY | ADDITIONAL_OPEN_FLAGS);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+
+  uint8_t *data = new uint8_t[st.st_size];
+  if (::read(fd, data, st.st_size) != st.st_size) {
+    ::perror("read");
+    return 1;
+  }
+  ::close(fd);
+
+  Context context;
+
+  FILE* out = NULL;
+  if (argc == 3)
+    out = fopen(argv[2], "wb");
+
+  ots::FILEStream output(out);
+  const bool result = context.Process(&output, data, st.st_size);
+
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+  }
+  return !result;
+}
diff --git a/third_party/ots/test/perf.cc b/third_party/ots/test/perf.cc
new file mode 100644
index 0000000..de7ee41
--- /dev/null
+++ b/third_party/ots/test/perf.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
+  return 1;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+
+  uint8_t *data = new uint8_t[st.st_size];
+  if (::read(fd, data, st.st_size) != st.st_size) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+
+  // A transcoded font is usually smaller than an original font.
+  // However, it can be slightly bigger than the original one due to
+  // name table replacement and/or padding for glyf table.
+  static const size_t kPadLen = 20 * 1024;
+  uint8_t *result = new uint8_t[st.st_size + kPadLen];
+
+  int num_repeat = 250;
+  if (st.st_size < 1024 * 1024) {
+    num_repeat = 2500;
+  }
+  if (st.st_size < 1024 * 100) {
+    num_repeat = 5000;
+  }
+
+  struct timeval start, end, elapsed;
+  ::gettimeofday(&start, 0);
+  for (int i = 0; i < num_repeat; ++i) {
+    ots::MemoryStream output(result, st.st_size + kPadLen);
+    ots::OTSContext context;
+    bool r = context.Process(&output, data, st.st_size);
+    if (!r) {
+      std::fprintf(stderr, "Failed to sanitise file!\n");
+      return 1;
+    }
+  }
+  ::gettimeofday(&end, 0);
+  timersub(&end, &start, &elapsed);
+
+  long long unsigned us
+      = ((elapsed.tv_sec * 1000 * 1000) + elapsed.tv_usec) / num_repeat;
+  std::fprintf(stderr, "%llu [us] %s (%llu bytes, %llu [byte/us])\n",
+               us, argv[1], static_cast<long long>(st.st_size),
+               (us ? st.st_size / us : 0));
+
+  return 0;
+}
diff --git a/third_party/ots/test/side-by-side.cc b/third_party/ots/test/side-by-side.cc
new file mode 100644
index 0000000..9034a7c
--- /dev/null
+++ b/third_party/ots/test/side-by-side.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+void DumpBitmap(const FT_Bitmap *bitmap) {
+  for (int i = 0; i < bitmap->rows * bitmap->width; ++i) {
+    if (bitmap->buffer[i] > 192) {
+      std::fprintf(stderr, "#");
+    } else if (bitmap->buffer[i] > 128) {
+      std::fprintf(stderr, "*");
+    } else if (bitmap->buffer[i] > 64) {
+      std::fprintf(stderr, "+");
+    } else if (bitmap->buffer[i] > 32) {
+      std::fprintf(stderr, ".");
+    } else {
+      std::fprintf(stderr, " ");
+    }
+
+    if ((i + 1) % bitmap->width == 0) {
+      std::fprintf(stderr, "\n");
+    }
+  }
+}
+
+int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) {
+  int ret = 0;
+
+  if (orig->width == trans->width &&
+      orig->rows == trans->rows) {
+    for (int i = 0; i < orig->rows * orig->width; ++i) {
+      if (orig->buffer[i] != trans->buffer[i]) {
+        std::fprintf(stderr, "bitmap data doesn't match!\n");
+        ret = 1;
+        break;
+      }
+    }
+  } else {
+    std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n",
+                 orig->width, orig->rows, trans->width, trans->rows);
+    ret = 1;
+  }
+
+  if (ret) {
+    std::fprintf(stderr, "EXPECTED:\n");
+    DumpBitmap(orig);
+    std::fprintf(stderr, "\nACTUAL:\n");
+    DumpBitmap(trans);
+    std::fprintf(stderr, "\n\n");
+  }
+
+  delete[] orig->buffer;
+  delete[] trans->buffer;
+  return ret;
+}
+
+int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) {
+  FT_BBox bbox;
+  FT_Outline_Get_CBox(outline, &bbox);
+
+  bbox.xMin &= ~63;
+  bbox.yMin &= ~63;
+  bbox.xMax = (bbox.xMax + 63) & ~63;
+  bbox.yMax = (bbox.yMax + 63) & ~63;
+  FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);
+
+  const int w = (bbox.xMax - bbox.xMin) >> 6;
+  const int h = (bbox.yMax - bbox.yMin) >> 6;
+
+  if (w == 0 || h == 0) {
+    return -1;  // white space
+  }
+  if (w < 0 || h < 0) {
+    std::fprintf(stderr, "bad width/height\n");
+    return 1;  // error
+  }
+
+  uint8_t *buf = new uint8_t[w * h];
+  std::memset(buf, 0x0, w * h);
+
+  bitmap->width = w;
+  bitmap->rows = h;
+  bitmap->pitch = w;
+  bitmap->buffer = buf;
+  bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
+  bitmap->num_grays = 256;
+  if (FT_Outline_Get_Bitmap(library, outline, bitmap)) {
+    std::fprintf(stderr, "can't get outline\n");
+    delete[] buf;
+    return 1;  // error.
+  }
+
+  return 0;
+}
+
+int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) {
+  static const int kDpi = 72;
+
+  FT_Matrix matrix;
+  matrix.xx = matrix.yy = 1 << 16;
+  matrix.xy = matrix.yx = 0 << 16;
+
+  FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
+  if (!use_bitmap) {
+    // Since the transcoder drops embedded bitmaps from the transcoded one,
+    // we have to use FT_LOAD_NO_BITMAP flag for the original face.
+    flags |= FT_LOAD_NO_BITMAP;
+  }
+
+  FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0);
+  if (error) {
+    std::fprintf(stderr, "Failed to set the char size!\n");
+    return 1;
+  }
+
+  FT_Set_Transform(face, &matrix, 0);
+
+  error = FT_Load_Char(face, c, flags);
+  if (error) return -1;  // no such glyf in the font.
+
+  if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
+    std::fprintf(stderr, "bad format\n");
+    return 1;
+  }
+
+  return 0;
+}
+
+int LoadCharThenCompare(FT_Library library,
+                        FT_Face orig_face, FT_Face trans_face,
+                        int pt, FT_ULong c) {
+  FT_Bitmap orig_bitmap, trans_bitmap;
+
+  // Load original bitmap.
+  int ret = LoadChar(orig_face, false, pt, c);
+  if (ret) return ret;  // 1: error, -1: no such glyph
+
+  FT_Outline *outline = &orig_face->glyph->outline;
+  ret = GetBitmap(library, outline, &orig_bitmap);
+  if (ret) return ret;  // white space?
+
+  // Load transformed bitmap.
+  ret = LoadChar(trans_face, true, pt, c);
+  if (ret == -1) {
+    std::fprintf(stderr, "the glyph is not found on the transcoded font\n");
+  }
+  if (ret) return 1;  // -1 should be treated as error.
+  outline = &trans_face->glyph->outline;
+  ret = GetBitmap(library, outline, &trans_bitmap);
+  if (ret) return ret;  // white space?
+
+  return CompareBitmaps(&orig_bitmap, &trans_bitmap);
+}
+
+int SideBySide(FT_Library library, const char *file_name,
+               uint8_t *orig_font, size_t orig_len,
+               uint8_t *trans_font, size_t trans_len) {
+  FT_Face orig_face;
+  FT_Error error
+      = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name);
+    return 1;
+  }
+
+  FT_Face trans_face;
+  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the transcoded font: %s!\n",
+                 file_name);
+    return 1;
+  }
+
+  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
+  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
+
+  static const int kUnicodeRanges[] = {
+    0x0020, 0x007E,  // Basic Latin (ASCII)
+    0x00A1, 0x017F,  // Latin-1
+    0x1100, 0x11FF,  // Hangul
+    0x3040, 0x309F,  // Japanese HIRAGANA letters
+    0x3130, 0x318F,  // Hangul
+    0x4E00, 0x4F00,  // CJK Kanji/Hanja
+    0xAC00, 0xAD00,  // Hangul
+  };
+  static const size_t kUnicodeRangesLen
+      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
+
+  for (size_t i = 0; i < kPtsLen; ++i) {
+    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
+      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
+        int ret = LoadCharThenCompare(library, orig_face, trans_face,
+                                      kPts[i],
+                                      kUnicodeRanges[j] + k);
+        if (ret > 0) {
+          std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n",
+                       file_name, kUnicodeRanges[j] + k, kPts[i]);
+          return 1;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
+    return 1;
+  }
+
+  // load the font to memory.
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+  const off_t orig_len = st.st_size;
+
+  uint8_t *orig_font = new uint8_t[orig_len];
+  if (::read(fd, orig_font, orig_len) != orig_len) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // check if FreeType2 can open the original font.
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+  FT_Face dummy;
+  error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the original font with FT2! %s\n",
+                 argv[1]);
+    return 1;
+  }
+
+  // transcode the original font.
+  static const size_t kPadLen = 20 * 1024;
+  uint8_t *trans_font = new uint8_t[orig_len + kPadLen];
+  ots::MemoryStream output(trans_font, orig_len + kPadLen);
+  ots::OTSContext context;
+
+  bool result = context.Process(&output, orig_font, orig_len);
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]);
+    return 1;
+  }
+  const size_t trans_len = output.Tell();
+
+  // perform side-by-side tests.
+  return SideBySide(library, argv[1],
+                    orig_font, orig_len,
+                    trans_font, trans_len);
+}
diff --git a/third_party/ots/test/table_dependencies_test.cc b/third_party/ots/test/table_dependencies_test.cc
new file mode 100644
index 0000000..bbaaa30
--- /dev/null
+++ b/third_party/ots/test/table_dependencies_test.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+
+#include "gsub.h"
+#include "ots.h"
+#include "ots-memory-stream.h"
+#include "vhea.h"
+#include "vmtx.h"
+
+#define SET_TABLE(name, capname) \
+  do { file.name = new ots::OpenType##capname; } while (0)
+#define SET_LAYOUT_TABLE(name, capname)                    \
+  do {                                                     \
+    if (!file.name) {                                      \
+      SET_TABLE(name, capname);                            \
+    }                                                      \
+    file.name->data = reinterpret_cast<const uint8_t*>(1); \
+    file.name->length = 1;                                 \
+  } while (0)
+#define DROP_TABLE(name) \
+  do { delete file.name; file.name = NULL; } while (0)
+#define DROP_LAYOUT_TABLE(name) \
+  do { file.name->data = NULL; file.name->length = 0; } while (0)
+
+namespace {
+
+class TableDependenciesTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    SET_LAYOUT_TABLE(gsub, GSUB);
+    SET_TABLE(vhea, VHEA);
+    SET_TABLE(vmtx, VMTX);
+  }
+
+  virtual void TearDown() {
+    DROP_TABLE(gsub);
+    DROP_TABLE(vhea);
+    DROP_TABLE(vmtx);
+  }
+  ots::OpenTypeFile file;
+};
+}  // namespace
+
+TEST_F(TableDependenciesTest, TestVhea) {
+  EXPECT_TRUE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtx) {
+  EXPECT_TRUE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaVmtx) {
+  DROP_TABLE(vmtx);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxVhea) {
+  DROP_TABLE(vhea);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaGsub) {
+  DROP_LAYOUT_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+  DROP_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxGsub) {
+  DROP_LAYOUT_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+  DROP_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
diff --git a/third_party/ots/test/test_malicious_fonts.sh b/third_party/ots/test/test_malicious_fonts.sh
new file mode 100755
index 0000000..7a35f26
--- /dev/null
+++ b/third_party/ots/test/test_malicious_fonts.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage: ./test_malicious_fonts.sh [ttf_or_otf_file_name]
+
+BASE_DIR=~/malicious/
+CHECKER=./validator-checker
+
+if [ ! -x "$CHECKER" ] ; then
+  echo "$CHECKER is not found."
+  exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+  # No font file is specified. Apply this script to all TT/OT files under the
+  # BASE_DIR.
+  if [ ! -d $BASE_DIR ] ; then
+    echo "$BASE_DIR does not exist."
+    exit 1
+  fi
+
+  # Recursively call this script.
+  find $BASE_DIR -type f -name '*tf' -exec "$0" {} \;
+  echo
+  exit 0
+fi
+
+if [ $# -gt 1 ] ; then
+  echo "Usage: $0 [ttf_or_otf_file_name]"
+  exit 1
+fi
+
+# Confirm that the malicious font file does not crash OTS nor OS font renderer. 
+base=`basename "$1"`
+"$CHECKER" "$1" > /dev/null 2>&1 || (echo ; echo "\nFAIL: $1 (Run $CHECKER $1 for more information.)")
+echo -n "."
diff --git a/third_party/ots/test/test_unmalicious_fonts.sh b/third_party/ots/test/test_unmalicious_fonts.sh
new file mode 100755
index 0000000..ae3a5bb
--- /dev/null
+++ b/third_party/ots/test/test_unmalicious_fonts.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage: ./test_unmalicious_fonts.sh [ttf_or_otf_file_name]
+
+BLACKLIST=./BLACKLIST.txt
+CHECKER=./idempotent
+
+if [ ! -r "$BLACKLIST" ] ; then
+  echo "$BLACKLIST is not found."
+  exit 1
+fi
+
+if [ ! -x "$CHECKER" ] ; then
+  echo "$CHECKER is not found."
+  exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+  # No font file is specified. Apply this script to all TT/OT files under the
+  # BASE_DIR below.
+
+  # On Ubuntu Linux (>= 8.04), You can install ~1800 TrueType/OpenType fonts
+  # to /usr/share/fonts/truetype by:
+  #   % sudo apt-get install ttf-.*[^0]$
+  BASE_DIR=/usr/share/fonts/truetype/
+  if [ ! -d $BASE_DIR ] ; then
+    # Mac OS X
+    BASE_DIR="/Library/Fonts/ /System/Library/Fonts/"
+  fi
+  # TODO(yusukes): Support Cygwin.
+
+  # Recursively call this script.
+  find $BASE_DIR -type f -name '*tf' -exec "$0" {} \;
+  echo
+  exit 0
+fi
+
+if [ $# -gt 1 ] ; then
+  echo "Usage: $0 [ttf_or_otf_file_name]"
+  exit 1
+fi
+
+# Check the font file using idempotent iff the font is not blacklisted.
+base=`basename "$1"`
+egrep -i -e "^$base" "$BLACKLIST" > /dev/null 2>&1 || "$CHECKER" "$1" > /dev/null 2>&1 || (echo ; echo "FAIL: $1 (Run $CHECKER $1 for more information.)")
+echo -n "."
diff --git a/third_party/ots/test/validator-checker.cc b/third_party/ots/test/validator-checker.cc
new file mode 100644
index 0000000..6cb5bca
--- /dev/null
+++ b/third_party/ots/test/validator-checker.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(_MSC_VER)
+#ifdef __linux__
+// Linux
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#else
+// Mac OS X
+#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
+#endif  // __linux__
+#else
+// Windows
+// TODO(yusukes): Support Windows.
+#endif  // _MSC_VER
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+#if !defined(_MSC_VER)
+#ifdef __linux__
+// Linux
+void LoadChar(FT_Face face, int pt, FT_ULong c) {
+  FT_Matrix matrix;
+  matrix.xx = matrix.yy = 1 << 16;
+  matrix.xy = matrix.yx = 0 << 16;
+
+  FT_Set_Char_Size(face, pt * (1 << 6), 0, 72, 0);
+  FT_Set_Transform(face, &matrix, 0);
+  FT_Load_Char(face, c, FT_LOAD_RENDER);
+}
+
+int OpenAndLoadChars(
+    const char *file_name, uint8_t *trans_font, size_t trans_len) {
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+
+  FT_Face trans_face;
+  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
+  if (error) {
+    std::fprintf(stderr,
+                 "OK: FreeType2 couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
+  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
+
+  static const int kUnicodeRanges[] = {
+    0x0020, 0x007E,  // Basic Latin (ASCII)
+    0x00A1, 0x017F,  // Latin-1
+    0x1100, 0x11FF,  // Hangul
+    0x3040, 0x309F,  // Japanese HIRAGANA letters
+    0x3130, 0x318F,  // Hangul
+    0x4E00, 0x4F00,  // CJK Kanji/Hanja
+    0xAC00, 0xAD00,  // Hangul
+  };
+  static const size_t kUnicodeRangesLen
+      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
+
+  for (size_t i = 0; i < kPtsLen; ++i) {
+    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
+      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
+        LoadChar(trans_face, kPts[i], kUnicodeRanges[j] + k);
+      }
+    }
+  }
+
+  std::fprintf(stderr, "OK: FreeType2 didn't crash: %s\n", file_name);
+  return 0;
+}
+#else
+// Mac OS X
+int OpenAndLoadChars(
+    const char *file_name, uint8_t *trans_font, size_t trans_len) {
+  CFDataRef data = CFDataCreate(0, trans_font, trans_len);
+  if (!data) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
+  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
+  CGDataProviderRelease(dataProvider);
+  CFRelease(data);
+  if (!cgFontRef) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
+  CGFontRelease(cgFontRef);
+  if (!numGlyphs) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+  std::fprintf(stderr, "OK: font renderer didn't crash: %s\n", file_name);
+  // TODO(yusukes): would be better to perform LoadChar() like Linux.
+  return 0;
+}
+#endif  // __linux__
+#else
+// Windows
+// TODO(yusukes): Support Windows.
+#endif  // _MSC_VER
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
+    return 1;
+  }
+
+  // load the font to memory.
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+  const off_t orig_len = st.st_size;
+
+  uint8_t *orig_font = new uint8_t[orig_len];
+  if (::read(fd, orig_font, orig_len) != orig_len) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // transcode the malicious font.
+  static const size_t kBigPadLen = 1024 * 1024;  // 1MB
+  uint8_t *trans_font = new uint8_t[orig_len + kBigPadLen];
+  ots::MemoryStream output(trans_font, orig_len + kBigPadLen);
+  ots::OTSContext context;
+
+  bool result = context.Process(&output, orig_font, orig_len);
+  if (!result) {
+    std::fprintf(stderr, "OK: the malicious font was filtered: %s\n", argv[1]);
+    return 0;
+  }
+  const size_t trans_len = output.Tell();
+
+  return OpenAndLoadChars(argv[1], trans_font, trans_len);
+}
diff --git a/third_party/ots/third_party/brotli.gyp b/third_party/ots/third_party/brotli.gyp
new file mode 100644
index 0000000..9903110
--- /dev/null
+++ b/third_party/ots/third_party/brotli.gyp
@@ -0,0 +1,32 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'brotli',
+      'type': 'static_library',
+      'include_dirs': [
+        'brotli/dec',
+      ],
+      'sources': [
+        'brotli/dec/bit_reader.c',
+        'brotli/dec/bit_reader.h',
+        'brotli/dec/context.h',
+        'brotli/dec/decode.c',
+        'brotli/dec/decode.h',
+        'brotli/dec/dictionary.h',
+        'brotli/dec/huffman.c',
+        'brotli/dec/huffman.h',
+        'brotli/dec/prefix.h',
+        'brotli/dec/safe_malloc.c',
+        'brotli/dec/safe_malloc.h',
+        'brotli/dec/streams.c',
+        'brotli/dec/streams.h',
+        'brotli/dec/transform.h',
+        'brotli/dec/types.h',
+      ],
+    },
+  ],
+}
diff --git a/third_party/ots/tools/ttf-checksum.py b/third_party/ots/tools/ttf-checksum.py
new file mode 100644
index 0000000..802e3cc
--- /dev/null
+++ b/third_party/ots/tools/ttf-checksum.py
@@ -0,0 +1,44 @@
+import struct
+import sys
+
+def readU32(contents, offset):
+  wordBytes = contents[offset:offset + 4]
+  return struct.unpack('>I', wordBytes)[0]
+
+def readU16(contents, offset):
+  wordBytes = contents[offset:offset + 2]
+  return struct.unpack('>H', wordBytes)[0]
+
+def checkChecksum(infile):
+  contents = infile.read()
+  if len(contents) % 4:
+    print 'File length is not a multiple of 4'
+
+  sum = 0
+  for offset in range(0, len(contents), 4):
+    sum += readU32(contents, offset)
+    while sum >= 2**32:
+      sum -= 2**32
+  print 'Sum of whole file: %x' % sum
+
+  numTables = readU16(contents, 4)
+
+  for offset in range(12, 12 + numTables * 16, 16):
+    tag = contents[offset:offset + 4]
+    chksum = readU32(contents, offset + 4)
+    toffset = readU32(contents, offset + 8)
+    tlength = readU32(contents, offset + 12)
+
+    sum = 0
+    for offset2 in range(toffset, toffset + tlength, 4):
+      sum += readU32(contents, offset2)
+      while sum >= 2**32:
+        sum -= 2**32
+    if sum != chksum:
+      print 'Bad chksum: %s' % tag
+
+if __name__ == '__main__':
+  if len(sys.argv) != 2:
+    print 'Usage: %s <ttf filename>' % sys.argv[0]
+  else:
+    checkChecksum(file(sys.argv[1], 'r'))
diff --git a/third_party/protobuf/BUILD.gn b/third_party/protobuf/BUILD.gn
index 1367d8a..6825f1e 100644
--- a/third_party/protobuf/BUILD.gn
+++ b/third_party/protobuf/BUILD.gn
@@ -17,11 +17,6 @@
     "GOOGLE_PROTOBUF_NO_RTTI",
     "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): http://crbug.com/167187 size_t -> int.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 if (component_mode == "shared_library") {
@@ -98,7 +93,11 @@
   if (is_win) {
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
-  public_configs = [ ":protobuf_config" ]
+  public_configs = [
+    ":protobuf_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   cflags = protobuf_lite_cflags
 
@@ -177,7 +176,11 @@
   if (is_win) {
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
-  public_configs = [ ":protobuf_config" ]
+  public_configs = [
+    ":protobuf_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   cflags = protobuf_lite_cflags
 }
diff --git a/third_party/qcms/BUILD.gn b/third_party/qcms/BUILD.gn
index b059319..ee62b8e 100644
--- a/third_party/qcms/BUILD.gn
+++ b/third_party/qcms/BUILD.gn
@@ -25,10 +25,10 @@
   configs += [ "//build/config/compiler:no_chromium_code" ]
   public_configs = [ ":qcms_config" ]
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     defines = [ "SSE2_ENABLE" ]
     sources += [ "src/transform-sse2.c" ]
-    if (!(is_win && cpu_arch == "x64")) {
+    if (!(is_win && current_cpu == "x64")) {
       # QCMS assumes this target isn't compiled since MSVC x64 doesn't support
       # the MMX intrinsics present in the SSE1 code.
       sources += [ "src/transform-sse1.c" ]
diff --git a/third_party/re2/BUILD.gn b/third_party/re2/BUILD.gn
index d022e4f..2bc130a 100644
--- a/third_party/re2/BUILD.gn
+++ b/third_party/re2/BUILD.gn
@@ -67,10 +67,7 @@
 
   if (is_win) {
     include_dirs = [ "mswin" ]
-    cflags = [
-      "/wd4267",  # Conversion from size_t.
-      "/wd4722",  # Destructor never terminates.
-    ]
+    cflags = [ "/wd4722" ]  # Destructor never terminates.
   } else {
     sources -= [ "mswin/stdint.h" ]
   }
diff --git a/third_party/smhasher/BUILD.gn b/third_party/smhasher/BUILD.gn
index e1bbb5e..fd9088c 100644
--- a/third_party/smhasher/BUILD.gn
+++ b/third_party/smhasher/BUILD.gn
@@ -27,9 +27,4 @@
   ]
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
-
-  if (is_win) {
-    # TODO(jschuh): http://code.google.com/p/smhasher/issues/detail?id=19
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn
index cd9b453..a09a495 100644
--- a/third_party/yasm/BUILD.gn
+++ b/third_party/yasm/BUILD.gn
@@ -30,18 +30,14 @@
 if (current_toolchain == host_toolchain) {
   # Various files referenced by multiple targets.
   yasm_gen_include_dir = "$target_gen_dir/include"
-  yasm_os = os
-  if (is_chromeos) {
-    yasm_os = "linux"
-  }
-  config_makefile = "source/config/$yasm_os/Makefile"
+  config_makefile = "source/config/$host_os/Makefile"
   version_file = "version.mac"
 
   import("//build/compiled_action.gni")
 
   config("yasm_config") {
     include_dirs = [
-      "source/config/$yasm_os",
+      "source/config/$host_os",
       "source/patched-yasm",
     ]
     defines = [ "HAVE_CONFIG_H" ]
@@ -148,8 +144,6 @@
     # re2c is missing CLOSEVOP from one switch.
     if (is_posix) {
       cflags = [ "-Wno-switch" ]
-    } else if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t to int conversion.
     }
   }
 
@@ -248,9 +242,7 @@
     # directory, but the gen_x86_insn.py script does not make this easy.
     include_dirs = [ yasm_gen_include_dir ]
 
-    if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t to int conversion.
-    } else {
+    if (!is_win) {
       cflags = [
         "-ansi",
         "-pedantic",
diff --git a/third_party/yasm/yasm_assemble.gni b/third_party/yasm/yasm_assemble.gni
index 56d7e11..1a84d51 100644
--- a/third_party/yasm/yasm_assemble.gni
+++ b/third_party/yasm/yasm_assemble.gni
@@ -42,13 +42,13 @@
 #   }
 
 if (is_mac || is_ios) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-fmacho32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-fmacho64",
       "-m",
@@ -56,13 +56,13 @@
     ]
   }
 } else if (is_posix) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-felf32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-DPIC",
       "-felf64",
@@ -71,14 +71,14 @@
     ]
   }
 } else if (is_win) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-DPREFIX",
       "-fwin32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-fwin64",
       "-m",
@@ -99,7 +99,7 @@
 
   # Only depend on YASM on x86 systems. Force compilation of .asm files for
   # ARM to fail.
-  assert(cpu_arch == "x86" || cpu_arch == "x64")
+  assert(current_cpu == "x86" || current_cpu == "x64")
 
   action_name = "${target_name}_action"
   source_set_name = target_name
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index e49b5e9..1cea9ac 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -7,7 +7,7 @@
 }
 
 static_library("zlib_x86_simd") {
-  if (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) {
+  if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources = [
       "crc_folding.c",
       "fill_window_sse.c",
@@ -65,7 +65,7 @@
     "zutil.h",
   ]
 
-  if (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) {
+  if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources += [ "x86.c" ]
   }
 
diff --git a/tools/android/common/BUILD.gn b/tools/android/common/BUILD.gn
index 8307a91..3c86741 100644
--- a/tools/android/common/BUILD.gn
+++ b/tools/android/common/BUILD.gn
@@ -12,4 +12,8 @@
     "net.cc",
     "net.h",
   ]
+
+  deps = [
+    "//base",
+  ]
 }
diff --git a/tools/android/memconsumer/memconsumer.gyp b/tools/android/memconsumer/memconsumer.gyp
index f721fc1..b0e2517 100644
--- a/tools/android/memconsumer/memconsumer.gyp
+++ b/tools/android/memconsumer/memconsumer.gyp
@@ -28,6 +28,11 @@
     {
       'target_name': 'libmemconsumer',
       'type': 'shared_library',
+      'variables': {
+        # This library uses native JNI exports; tell gyp so that the required
+        # symbols will be kept.
+        'use_native_jni_exports': 1,
+      },
       'sources': [
         'memconsumer_hook.cc',
       ],
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
index 473ad20..7dae381 100644
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
@@ -470,12 +470,17 @@
     if (!fn || !Config::IsTraceMethod(fn, nullptr))
       return false;
 
-    // Currently, a manually dispatched class cannot have mixin bases (having
-    // one would add a vtable which we explicitly check against). This means
-    // that we can only make calls to a trace method of the same name. Revisit
-    // this if our mixin/vtable assumption changes.
-    if (fn->getName() != trace_->getName())
-      return false;
+    if (trace_->getName() == kTraceImplName) {
+      if (fn->getName() != kTraceName)
+        return false;
+    } else {
+      // Currently, a manually dispatched class cannot have mixin bases (having
+      // one would add a vtable which we explicitly check against). This means
+      // that we can only make calls to a trace method of the same name. Revisit
+      // this if our mixin/vtable assumption changes.
+      if (fn->getName() != trace_->getName())
+        return false;
+    }
 
     CXXRecordDecl* decl = 0;
     if (callee && callee->hasQualifier()) {
diff --git a/tools/clang/blink_gc_plugin/Config.h b/tools/clang/blink_gc_plugin/Config.h
index d508fda..814a4ac 100644
--- a/tools/clang/blink_gc_plugin/Config.h
+++ b/tools/clang/blink_gc_plugin/Config.h
@@ -195,7 +195,7 @@
                        formal_type.getTypePtr())) {
       if (parm_type->getDecl()->getName() == kVisitorDispatcherName) {
         // Unresolved, but its parameter name is VisitorDispatcher.
-        return false;
+        return true;
       }
     }
 
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl.cpp b/tools/clang/blink_gc_plugin/tests/traceimpl.cpp
index 61d8560..c8849cc 100644
--- a/tools/clang/blink_gc_plugin/tests/traceimpl.cpp
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl.cpp
@@ -12,6 +12,17 @@
 
 template <typename VisitorDispatcher>
 inline void TraceImplExtern::traceImpl(VisitorDispatcher visitor) {
-  visitor->trace(m_x);
+  visitor->trace(x_);
 }
+
+void TraceImplBaseExtern::trace(Visitor* visitor) {
+  traceImpl(visitor);
+}
+
+template <typename VisitorDispatcher>
+inline void TraceImplBaseExtern::traceImpl(VisitorDispatcher visitor) {
+  visitor->trace(x_);
+  Base::trace(visitor);
+}
+
 }
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl.h b/tools/clang/blink_gc_plugin/tests/traceimpl.h
index 26f4615..64fae26 100644
--- a/tools/clang/blink_gc_plugin/tests/traceimpl.h
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl.h
@@ -20,11 +20,11 @@
 
   template <typename VisitorDispatcher>
   void traceImpl(VisitorDispatcher visitor) {
-    visitor->trace(m_x);
+    visitor->trace(x_);
   }
 
  private:
-  Member<X> m_x;
+  Member<X> x_;
 };
 
 class TraceImplExtern : public GarbageCollected<TraceImplExtern> {
@@ -34,8 +34,35 @@
   inline void traceImpl(VisitorDispatcher);
 
  private:
-  Member<X> m_x;
+  Member<X> x_;
 };
+
+class Base : public GarbageCollected<Base> {
+ public:
+  virtual void trace(Visitor* visitor) {}
+};
+
+class TraceImplBaseInlined : public Base {
+ public:
+  void trace(Visitor* visitor) override { traceImpl(visitor); }
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor) {
+    Base::trace(visitor);
+  }
+};
+
+class TraceImplBaseExtern : public Base {
+ public:
+  void trace(Visitor* visitor) override;
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher);
+
+ private:
+  Member<X> x_;
+};
+
 }
 
 #endif
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl_error.cpp b/tools/clang/blink_gc_plugin/tests/traceimpl_error.cpp
new file mode 100644
index 0000000..041c565
--- /dev/null
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl_error.cpp
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "traceimpl_error.h"
+
+namespace blink {
+
+void TraceImplExternWithUntracedMember::trace(Visitor* visitor) {
+  traceImpl(visitor);
+}
+
+template <typename VisitorDispatcher>
+inline void TraceImplExternWithUntracedMember::traceImpl(
+    VisitorDispatcher visitor) {
+  // Should get a warning as well.
+}
+
+void TraceImplExternWithUntracedBase::trace(Visitor* visitor) {
+  traceImpl(visitor);
+}
+
+template <typename VisitorDispatcher>
+inline void TraceImplExternWithUntracedBase::traceImpl(
+    VisitorDispatcher visitor) {
+  // Ditto.
+}
+
+}
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl_error.h b/tools/clang/blink_gc_plugin/tests/traceimpl_error.h
new file mode 100644
index 0000000..5a883b4
--- /dev/null
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl_error.h
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TRACEIMPL_ERROR_H_
+#define TRACEIMPL_ERROR_H_
+
+#include "heap/stubs.h"
+
+namespace blink {
+
+class X : public GarbageCollected<X> {
+ public:
+  virtual void trace(Visitor*) {}
+};
+
+class TraceImplInlinedWithUntracedMember
+    : public GarbageCollected<TraceImplInlinedWithUntracedMember> {
+ public:
+  void trace(Visitor* visitor) { traceImpl(visitor); }
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor) {
+    // Empty; should get complaints from the plugin for untraced x_.
+  }
+
+ private:
+  Member<X> x_;
+};
+
+class TraceImplExternWithUntracedMember
+    : public GarbageCollected<TraceImplExternWithUntracedMember> {
+ public:
+  void trace(Visitor* visitor);
+
+  template <typename VisitorDispatcher>
+  inline void traceImpl(VisitorDispatcher);
+
+ private:
+  Member<X> x_;
+};
+
+class Base : public GarbageCollected<Base> {
+ public:
+  virtual void trace(Visitor*) {}
+};
+
+class TraceImplInlineWithUntracedBase : public Base {
+ public:
+  void trace(Visitor* visitor) override { traceImpl(visitor); }
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor) {
+    // Empty; should get complaints from the plugin for untraced Base.
+  }
+};
+
+class TraceImplExternWithUntracedBase : public Base {
+ public:
+  void trace(Visitor*) override;
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor);
+};
+
+}
+
+#endif  // TRACEIMPL_ERROR_H_
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl_error.txt b/tools/clang/blink_gc_plugin/tests/traceimpl_error.txt
new file mode 100644
index 0000000..070b029
--- /dev/null
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl_error.txt
@@ -0,0 +1,20 @@
+In file included from traceimpl_error.cpp:5:
+./traceimpl_error.h:23:3: warning: [blink-gc] Class 'TraceImplInlinedWithUntracedMember' has untraced fields that require tracing.
+  void traceImpl(VisitorDispatcher visitor) {
+  ^
+./traceimpl_error.h:28:3: note: [blink-gc] Untraced field 'x_' declared here:
+  Member<X> x_;
+  ^
+./traceimpl_error.h:53:3: warning: [blink-gc] Base class 'Base' of derived class 'TraceImplInlineWithUntracedBase' requires tracing.
+  void traceImpl(VisitorDispatcher visitor) {
+  ^
+traceimpl_error.cpp:14:1: warning: [blink-gc] Class 'TraceImplExternWithUntracedMember' has untraced fields that require tracing.
+inline void TraceImplExternWithUntracedMember::traceImpl(
+^
+./traceimpl_error.h:40:3: note: [blink-gc] Untraced field 'x_' declared here:
+  Member<X> x_;
+  ^
+traceimpl_error.cpp:24:1: warning: [blink-gc] Base class 'Base' of derived class 'TraceImplExternWithUntracedBase' requires tracing.
+inline void TraceImplExternWithUntracedBase::traceImpl(
+^
+4 warnings generated.
diff --git a/tools/clang/plugins/FindBadConstructsAction.cpp b/tools/clang/plugins/FindBadConstructsAction.cpp
index f302fd1..6c39d72 100644
--- a/tools/clang/plugins/FindBadConstructsAction.cpp
+++ b/tools/clang/plugins/FindBadConstructsAction.cpp
@@ -53,8 +53,6 @@
       // TODO(tsepez): Enable this by default once http://crbug.com/356815
       // and http://crbug.com/356816 are fixed.
       options_.check_enum_last_value = true;
-    } else if (args[i] == "strict-virtual-specifiers") {
-      options_.strict_virtual_specifiers = true;
     } else if (args[i] == "with-ast-visitor") {
       options_.with_ast_visitor = true;
     } else if (args[i] == "check-templates") {
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp
index 46e1df8..fe868c0 100644
--- a/tools/clang/plugins/FindBadConstructsConsumer.cpp
+++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp
@@ -283,6 +283,9 @@
       for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
            it != record->ctor_end();
            ++it) {
+        // The current check is buggy. An implicit copy constructor does not
+        // have an inline body, so this check never fires for classes with a
+        // user-declared out-of-line constructor.
         if (it->hasInlineBody()) {
           if (it->isCopyConstructor() &&
               !record->hasUserDeclaredCopyConstructor()) {
@@ -293,6 +296,15 @@
             emitWarning(it->getInnerLocStart(),
                         "Complex constructor has an inlined body.");
           }
+        } else if (it->isInlined() && (!it->isCopyOrMoveConstructor() ||
+                                       it->isExplicitlyDefaulted())) {
+          // isInlined() is a more reliable check than hasInlineBody(), but
+          // unfortunately, it results in warnings for implicit copy/move
+          // constructors in the previously mentioned situation. To preserve
+          // compatibility with existing Chromium code, only warn if it's an
+          // explicitly defaulted copy or move constructor.
+          emitWarning(it->getInnerLocStart(),
+                      "Complex constructor has an inlined body.");
         }
       }
     }
@@ -306,7 +318,7 @@
                   "Complex class/struct needs an explicit out-of-line "
                   "destructor.");
     } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
-      if (dtor->hasInlineBody()) {
+      if (dtor->isInlined()) {
         emitWarning(dtor->getInnerLocStart(),
                     "Complex destructor has an inline body.");
       }
@@ -331,8 +343,7 @@
         // magic to try to make sure SetUp()/TearDown() aren't capitalized
         // incorrectly, but having the plugin enforce override is also nice.
         (InTestingNamespace(overridden) &&
-         (!options_.strict_virtual_specifiers ||
-          !IsGtestTestFixture(overridden->getParent())))) {
+         !IsGtestTestFixture(overridden->getParent()))) {
       return true;
     }
   }
@@ -393,20 +404,13 @@
   OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
   FinalAttr* final_attr = method->getAttr<FinalAttr>();
 
-  if (method->isPure() && !options_.strict_virtual_specifiers)
-    return;
-
   if (IsMethodInBannedOrTestingNamespace(method))
     return;
 
-  if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers)
-    return;
-
   SourceManager& manager = instance().getSourceManager();
 
   // Complain if a method is annotated virtual && (override || final).
-  if (has_virtual && (override_attr || final_attr) &&
-      options_.strict_virtual_specifiers) {
+  if (has_virtual && (override_attr || final_attr)) {
     diagnostic().Report(method->getLocStart(),
                         diag_redundant_virtual_specifier_)
         << "'virtual'"
@@ -436,14 +440,14 @@
     }
   }
 
-  if (final_attr && override_attr && options_.strict_virtual_specifiers) {
+  if (final_attr && override_attr) {
     diagnostic().Report(override_attr->getLocation(),
                         diag_redundant_virtual_specifier_)
         << override_attr << final_attr
         << FixItHint::CreateRemoval(override_attr->getRange());
   }
 
-  if (final_attr && !is_override && options_.strict_virtual_specifiers) {
+  if (final_attr && !is_override) {
     diagnostic().Report(method->getLocStart(),
                         diag_base_method_virtual_and_final_)
         << FixItRemovalForVirtual(manager, method)
diff --git a/tools/clang/plugins/Options.h b/tools/clang/plugins/Options.h
index 5ef4977..112ec10 100644
--- a/tools/clang/plugins/Options.h
+++ b/tools/clang/plugins/Options.h
@@ -11,13 +11,11 @@
   Options()
       : check_base_classes(false),
         check_enum_last_value(false),
-        strict_virtual_specifiers(false),
         with_ast_visitor(false),
         check_templates(false) {}
 
   bool check_base_classes;
   bool check_enum_last_value;
-  bool strict_virtual_specifiers;
   bool with_ast_visitor;
   bool check_templates;
 };
diff --git a/tools/clang/plugins/tests/base_refcounted.cpp b/tools/clang/plugins/tests/base_refcounted.cpp
index 698bf7b..46e8975 100644
--- a/tools/clang/plugins/tests/base_refcounted.cpp
+++ b/tools/clang/plugins/tests/base_refcounted.cpp
@@ -13,7 +13,7 @@
     : public ProtectedRefCountedVirtualDtorInHeader {
  public:
   AnonymousDerivedProtectedToPublicInImpl() {}
-  virtual ~AnonymousDerivedProtectedToPublicInImpl() {}
+  ~AnonymousDerivedProtectedToPublicInImpl() override {}
 };
 
 // Unsafe; but we should only warn on the base class.
diff --git a/tools/clang/plugins/tests/base_refcounted.h b/tools/clang/plugins/tests/base_refcounted.h
index 4b4077c..4a489ee 100644
--- a/tools/clang/plugins/tests/base_refcounted.h
+++ b/tools/clang/plugins/tests/base_refcounted.h
@@ -107,7 +107,7 @@
     : public ProtectedRefCountedVirtualDtorInHeader {
  public:
   DerivedProtectedToPublicInHeader() {}
-  virtual ~DerivedProtectedToPublicInHeader() {}
+  ~DerivedProtectedToPublicInHeader() override {}
 };
 
 // Unsafe; A grandchild ends up implicitly exposing their parent and
@@ -146,10 +146,10 @@
     : public APublicInterface,
       public base::RefCounted<ImplementsAPublicInterface> {
  public:
-  virtual void DoFoo() override {}
+  void DoFoo() override {}
 
  protected:
-  virtual ~ImplementsAPublicInterface() {}
+  ~ImplementsAPublicInterface() override {}
 
  private:
   friend class base::RefCounted<ImplementsAPublicInterface>;
@@ -165,7 +165,7 @@
     : public AnImplicitInterface,
       public base::RefCounted<ImplementsAnImplicitInterface> {
  public:
-  virtual void DoBar() override {}
+  void DoBar() override {}
 
  private:
   friend class base::RefCounted<ImplementsAnImplicitInterface>;
@@ -177,11 +177,11 @@
     : private APublicInterface,
       public base::RefCounted<PrivatelyImplementsAPublicInterface> {
  public:
-  virtual void DoFoo() override {}
+  void DoFoo() override {}
 
  private:
   friend class base::RefCounted<PrivatelyImplementsAPublicInterface>;
-  virtual ~PrivatelyImplementsAPublicInterface() {}
+  ~PrivatelyImplementsAPublicInterface() override {}
 };
 
 // Unsafe.
@@ -192,7 +192,7 @@
 };
 class DerivedInterface : public BaseInterface {
  protected:
-  virtual ~DerivedInterface() {}
+  ~DerivedInterface() override {}
 };
 class SomeOtherInterface {
  public:
@@ -211,13 +211,13 @@
       public RefcountedType {
  public:
   // DerivedInterface
-  virtual void DoFoo() override {}
+  void DoFoo() override {}
 
   // SomeOtherInterface
-  virtual void DoBar() override {}
+  void DoBar() override {}
 
  protected:
-  virtual ~UnsafeInheritanceChain() {}
+  ~UnsafeInheritanceChain() override {}
 };
 
 #endif  // BASE_REFCOUNTED_H_
diff --git a/tools/clang/plugins/tests/base_refcounted.txt b/tools/clang/plugins/tests/base_refcounted.txt
index 20c0cdf..ae91986 100644
--- a/tools/clang/plugins/tests/base_refcounted.txt
+++ b/tools/clang/plugins/tests/base_refcounted.txt
@@ -15,7 +15,7 @@
   ~ProtectedRefCountedDtorInHeader() {}
   ^
 ./base_refcounted.h:110:3: warning: [chromium-style] Classes that are ref-counted should have destructors that are declared protected or private.
-  virtual ~DerivedProtectedToPublicInHeader() {}
+  ~DerivedProtectedToPublicInHeader() override {}
   ^
 ./base_refcounted.h:107:7: note: [chromium-style] 'DerivedProtectedToPublicInHeader' inherits from 'ProtectedRefCountedVirtualDtorInHeader' here
     : public ProtectedRefCountedVirtualDtorInHeader {
@@ -61,7 +61,7 @@
   ^
 ./base_refcounted.h:204:3: warning: [chromium-style] Classes that are ref-counted and have non-private destructors should declare their destructor virtual.
 base_refcounted.cpp:16:3: warning: [chromium-style] Classes that are ref-counted should have destructors that are declared protected or private.
-  virtual ~AnonymousDerivedProtectedToPublicInImpl() {}
+  ~AnonymousDerivedProtectedToPublicInImpl() override {}
   ^
 base_refcounted.cpp:13:7: note: [chromium-style] 'AnonymousDerivedProtectedToPublicInImpl' inherits from 'ProtectedRefCountedVirtualDtorInHeader' here
     : public ProtectedRefCountedVirtualDtorInHeader {
diff --git a/tools/clang/plugins/tests/missing_ctor.h b/tools/clang/plugins/tests/missing_ctor.h
index 1050457..0551fd7 100644
--- a/tools/clang/plugins/tests/missing_ctor.h
+++ b/tools/clang/plugins/tests/missing_ctor.h
@@ -8,6 +8,8 @@
 #include <string>
 #include <vector>
 
+// Note: this should warn for an implicit copy constructor too, but currently
+// doesn't, due to a plugin bug.
 class MissingCtorsArentOKInHeader {
  public:
 
@@ -16,4 +18,33 @@
   std::vector<std::string> two_;
 };
 
+// Inline move ctors shouldn't be warned about. Similar to the previous test
+// case, this also incorrectly fails to warn for the implicit copy ctor.
+class InlineImplicitMoveCtorOK {
+ public:
+  InlineImplicitMoveCtorOK();
+
+ private:
+  // ctor weight = 12, dtor weight = 9.
+  std::string one_;
+  std::string two_;
+  std::string three_;
+  int four_;
+  int five_;
+  int six_;
+};
+
+class ExplicitlyDefaultedInlineAlsoWarns {
+ public:
+  ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ~ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ExplicitlyDefaultedInlineAlsoWarns(
+      const ExplicitlyDefaultedInlineAlsoWarns&) = default;
+
+ private:
+  std::vector<int> one_;
+  std::vector<std::string> two_;
+
+};
+
 #endif  // MISSING_CTOR_H_
diff --git a/tools/clang/plugins/tests/missing_ctor.txt b/tools/clang/plugins/tests/missing_ctor.txt
index 301449c..0bc5696 100644
--- a/tools/clang/plugins/tests/missing_ctor.txt
+++ b/tools/clang/plugins/tests/missing_ctor.txt
@@ -1,6 +1,15 @@
 In file included from missing_ctor.cpp:5:
-./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
+./missing_ctor.h:13:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
 class MissingCtorsArentOKInHeader {
 ^
-./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
-2 warnings generated.
+./missing_ctor.h:13:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
+./missing_ctor.h:39:3: warning: [chromium-style] Complex constructor has an inlined body.
+  ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ^
+./missing_ctor.h:41:3: warning: [chromium-style] Complex constructor has an inlined body.
+  ExplicitlyDefaultedInlineAlsoWarns(
+  ^
+./missing_ctor.h:40:3: warning: [chromium-style] Complex destructor has an inline body.
+  ~ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ^
+5 warnings generated.
diff --git a/tools/clang/plugins/tests/overridden_methods.cpp b/tools/clang/plugins/tests/overridden_methods.cpp
index 398d6a4..8b0340c 100644
--- a/tools/clang/plugins/tests/overridden_methods.cpp
+++ b/tools/clang/plugins/tests/overridden_methods.cpp
@@ -11,19 +11,19 @@
 
 class ImplementationInterimClass : public BaseClass {
  public:
-  // Should not warn about pure virtual methods.
+  // Should warn about pure virtual methods.
   virtual void SomeMethod() = 0;
 };
 
 class ImplementationDerivedClass : public ImplementationInterimClass,
                                    public webkit_glue::WebKitObserverImpl {
  public:
-  // Should not warn about destructors.
+  // Should warn about destructors.
   virtual ~ImplementationDerivedClass() {}
   // Should warn.
   virtual void SomeMethod();
   // Should not warn if marked as override.
-  virtual void SomeOtherMethod() override;
+  void SomeOtherMethod() override;
   // Should not warn for inline implementations in implementation files.
   virtual void SomeInlineMethod() {}
   // Should not warn if overriding a method whose origin is blink.
diff --git a/tools/clang/plugins/tests/overridden_methods.h b/tools/clang/plugins/tests/overridden_methods.h
index c5af914..69ee100 100644
--- a/tools/clang/plugins/tests/overridden_methods.h
+++ b/tools/clang/plugins/tests/overridden_methods.h
@@ -21,7 +21,7 @@
 };
 
 class InterimClass : public BaseClass {
-  // Should not warn about pure virtual methods.
+  // Should warn about pure virtual methods.
   virtual void SomeMethod() = 0;
 };
 
@@ -42,12 +42,12 @@
 class DerivedClass : public InterimClass,
                      public webkit_glue::WebKitObserverImpl {
  public:
-  // Should not warn about destructors.
+  // Should warn about destructors.
   virtual ~DerivedClass() {}
   // Should warn.
   virtual void SomeMethod();
   // Should not warn if marked as override.
-  virtual void SomeOtherMethod() override;
+  void SomeOtherMethod() override;
   // Should warn for inline implementations.
   virtual void SomeInlineMethod() {}
   // Should not warn if overriding a method whose origin is blink.
diff --git a/tools/clang/plugins/tests/overridden_methods.txt b/tools/clang/plugins/tests/overridden_methods.txt
index 3ee0333..69ff2b1 100644
--- a/tools/clang/plugins/tests/overridden_methods.txt
+++ b/tools/clang/plugins/tests/overridden_methods.txt
@@ -1,4 +1,12 @@
 In file included from overridden_methods.cpp:5:
+./overridden_methods.h:25:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual void SomeMethod() = 0;
+                           ^
+                            override
+./overridden_methods.h:46:26: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual ~DerivedClass() {}
+                         ^
+                          override
 ./overridden_methods.h:48:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethod();
                            ^
@@ -31,6 +39,14 @@
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
                                              ^
                                               override
+overridden_methods.cpp:15:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual void SomeMethod() = 0;
+                           ^
+                            override
+overridden_methods.cpp:22:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual ~ImplementationDerivedClass() {}
+                                       ^
+                                        override
 overridden_methods.cpp:24:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethod();
                            ^
@@ -63,4 +79,4 @@
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
                                              ^
                                               override
-16 warnings generated.
+20 warnings generated.
diff --git a/tools/clang/plugins/tests/virtual_base_method_also_final.flags b/tools/clang/plugins/tests/virtual_base_method_also_final.flags
deleted file mode 100644
index a8915fc..0000000
--- a/tools/clang/plugins/tests/virtual_base_method_also_final.flags
+++ /dev/null
@@ -1 +0,0 @@
--Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
diff --git a/tools/clang/plugins/tests/virtual_bodies.cpp b/tools/clang/plugins/tests/virtual_bodies.cpp
index 8815dc2..4f37a67 100644
--- a/tools/clang/plugins/tests/virtual_bodies.cpp
+++ b/tools/clang/plugins/tests/virtual_bodies.cpp
@@ -16,13 +16,13 @@
 // Stubs to fill in the abstract method
 class ConcreteVirtualMethodsInHeaders : public VirtualMethodsInHeaders {
  public:
-  virtual void MethodIsAbstract() override {}
+  void MethodIsAbstract() override {}
 };
 
 class ConcreteVirtualMethodsInImplementation
     : public VirtualMethodsInImplementation {
  public:
-  virtual void MethodIsAbstract() override {}
+  void MethodIsAbstract() override {}
 };
 
 // Fill in the implementations
diff --git a/tools/clang/plugins/tests/virtual_specifiers.flags b/tools/clang/plugins/tests/virtual_specifiers.flags
deleted file mode 100644
index a8915fc..0000000
--- a/tools/clang/plugins/tests/virtual_specifiers.flags
+++ /dev/null
@@ -1 +0,0 @@
--Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
diff --git a/tools/clang/plugins/tests/weak_ptr_factory.flags b/tools/clang/plugins/tests/weak_ptr_factory.flags
index 7d9476e..52b35b5 100644
--- a/tools/clang/plugins/tests/weak_ptr_factory.flags
+++ b/tools/clang/plugins/tests/weak_ptr_factory.flags
@@ -1 +1 @@
--Xclang -plugin-arg-find-bad-constructs -Xclang check-templates -Xclang -plugin-arg-find-bad-constructs -Xclang check-weak-ptr-factory-order
+-Xclang -plugin-arg-find-bad-constructs -Xclang check-templates
diff --git a/tools/clang/scripts/plugin_flags.sh b/tools/clang/scripts/plugin_flags.sh
index 76a82c5..81fe0f1 100755
--- a/tools/clang/scripts/plugin_flags.sh
+++ b/tools/clang/scripts/plugin_flags.sh
@@ -17,5 +17,6 @@
 fi
 
 echo -Xclang -load -Xclang $CLANG_LIB_PATH/libFindBadConstructs.$LIBSUFFIX \
-  -Xclang -add-plugin -Xclang find-bad-constructs -Xclang \
-  -plugin-arg-find-bad-constructs -Xclang check-weak-ptr-factory-order
+  -Xclang -add-plugin -Xclang find-bad-constructs \
+  -Xclang -plugin-arg-find-bad-constructs -Xclang check-weak-ptr-factory-order \
+  -Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 6e55d0c..cb0878d 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -24,7 +24,7 @@
 # in bringup. Use a pinned revision to make it slightly more stable.
 if (re.search(r'\b(asan)=1', os.environ.get('GYP_DEFINES', '')) and
     not 'LLVM_FORCE_HEAD_REVISION' in os.environ):
-  LLVM_WIN_REVISION = '228702'
+  LLVM_WIN_REVISION = '229860'
 
 # Path constants. (All of these should be absolute paths.)
 THIS_DIR = os.path.abspath(os.path.dirname(__file__))
diff --git a/tools/git/move_source_file.py b/tools/git/move_source_file.py
index 1a25e44..18ef1ce 100755
--- a/tools/git/move_source_file.py
+++ b/tools/git/move_source_file.py
@@ -31,6 +31,7 @@
   # classpath.
   sys.path.append(os.path.abspath(os.path.join(sys.path[0], '..')))
 sort_headers = __import__('sort-headers')
+import sort_sources
 
 
 HANDLED_EXTENSIONS = ['.cc', '.mm', '.h', '.hh', '.cpp']
@@ -142,11 +143,13 @@
   from_rest = from_path
   to_rest = to_path
   while True:
-    mffr.MultiFileFindReplace(
+    files_with_changed_sources = mffr.MultiFileFindReplace(
         r'([\'"])%s([\'"])' % from_rest,
         r'\1%s\2' % to_rest,
         [os.path.join(visiting_directory, 'BUILD.gn'),
          os.path.join(visiting_directory, '*.gyp*')])
+    for changed_file in files_with_changed_sources:
+      sort_sources.ProcessFile(changed_file, should_confirm=False)
     from_first, from_rest = SplitByFirstComponent(from_rest)
     to_first, to_rest = SplitByFirstComponent(to_rest)
     visiting_directory = os.path.join(visiting_directory, from_first)
diff --git a/tools/relocation_packer/BUILD.gn b/tools/relocation_packer/BUILD.gn
index 0b29c91..e95dbcf 100644
--- a/tools/relocation_packer/BUILD.gn
+++ b/tools/relocation_packer/BUILD.gn
@@ -7,9 +7,9 @@
 
 assert(relocation_packing_supported)
 
-if (target_arch == "arm") {
+if (target_cpu == "arm") {
   target_define = "TARGET_ARM"
-} else if (target_arch == "arm64") {
+} else if (target_cpu == "arm64") {
   target_define = "TARGET_ARM64"
 }
 
@@ -78,7 +78,7 @@
 }
 
 if (current_toolchain == default_toolchain &&
-    (target_arch == "arm" || target_arch == "arm64")) {
+    (target_cpu == "arm" || target_cpu == "arm64")) {
   # Targets to build test data.  These participate only in building test
   # data for use with elf_file_unittest.cc, and are not part of the main
   # relocation packer build.  Unit test data files are checked in to the
@@ -102,11 +102,11 @@
   action("relocation_packer_unittests_test_data") {
     script = "test_data/generate_elf_file_unittest_relocs.py"
     test_file = "$root_build_dir/librelocation_packer_test_data.so"
-    if (target_arch == "arm") {
+    if (target_cpu == "arm") {
       added_section = ".android.rel.dyn"
       packed_output = "elf_file_unittest_relocs_arm32_packed.so"
       unpacked_output = "elf_file_unittest_relocs_arm32.so"
-    } else if (target_arch == "arm64") {
+    } else if (target_cpu == "arm64") {
       added_section = ".android.rela.dyn"
       packed_output = "elf_file_unittest_relocs_arm64_packed.so"
       unpacked_output = "elf_file_unittest_relocs_arm64.so"
diff --git a/tools/relocation_packer/config.gni b/tools/relocation_packer/config.gni
index 90e3979..4cdd945 100644
--- a/tools/relocation_packer/config.gni
+++ b/tools/relocation_packer/config.gni
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-relocation_packing_supported = target_arch == "arm" || target_arch == "arm64"
+relocation_packing_supported = target_cpu == "arm" || target_cpu == "arm64"
 
 if (relocation_packing_supported) {
   relocation_packer_target = "//tools/relocation_packer($host_toolchain)"
@@ -10,9 +10,9 @@
       get_label_info("$relocation_packer_target", "root_out_dir")
   relocation_packer_exe = "${relocation_packer_dir}/relocation_packer"
 
-  if (target_arch == "arm") {
+  if (target_cpu == "arm") {
     relocations_have_addends = 0
-  } else if (target_arch == "arm64") {
+  } else if (target_cpu == "arm64") {
     relocations_have_addends = 1
   }
 } else {
diff --git a/tools/valgrind/drmemory/suppressions_full.txt b/tools/valgrind/drmemory/suppressions_full.txt
index d2db1bf..eae85e5 100644
--- a/tools/valgrind/drmemory/suppressions_full.txt
+++ b/tools/valgrind/drmemory/suppressions_full.txt
@@ -1103,13 +1103,6 @@
 # at the peak set of tests we plan to run and remove the unused ones.
 
 UNINITIALIZED READ
-name=bug_99307
-*!modp_b64_encode
-*!base::Base64Encode*
-*!web_ui_util::GetImageDataUrl
-*!::NetworkInfoDictionary::set_icon
-
-UNINITIALIZED READ
 name=bug_101781
 *!encode_one_block
 *!encode_mcu_huff
diff --git a/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
index bdc0e84..e7f3488 100644
--- a/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
@@ -26,3 +26,6 @@
 PixelResourceTest/LayerTreeHostMasksPixelTest.MaskOfReplica/6
 PixelResourceTest/LayerTreeHostMasksPixelTest.MaskOfReplicaOfClippedLayer/5
 PixelResourceTest/LayerTreeHostMasksPixelTest.MaskOfReplicaOfClippedLayer/6
+
+# https://crbug.com/460581
+LayerTreeHostPictureTestRSLLMembershipWithScale.RunMultiThread_DirectRenderer_ImplSidePaint
diff --git a/tools/valgrind/gtest_exclude/content_unittests.gtest.txt b/tools/valgrind/gtest_exclude/content_unittests.gtest.txt
index d47ad75..60e98f2 100644
--- a/tools/valgrind/gtest_exclude/content_unittests.gtest.txt
+++ b/tools/valgrind/gtest_exclude/content_unittests.gtest.txt
@@ -10,3 +10,7 @@
 
 # https://crbug.com/449103
 WebInputEventAuraTest.TestMakeWebKeyboardEventWindowsKeyCode
+
+# Flaky: https://crbug.com/460578
+DesktopCaptureDeviceTest.InvertedFrame
+DesktopCaptureDeviceTest.UnpackedFrame
diff --git a/tools/valgrind/gtest_exclude/installer_util_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/installer_util_unittests.gtest-drmemory_win32.txt
new file mode 100644
index 0000000..7428628
--- /dev/null
+++ b/tools/valgrind/gtest_exclude/installer_util_unittests.gtest-drmemory_win32.txt
@@ -0,0 +1,2 @@
+# https://crbug.com/460584
+CopyTreeWorkItemTest.NewNameAndCopyTest
diff --git a/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt b/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt
index 6491f45..c322014 100644
--- a/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt
+++ b/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt
@@ -1,5 +1,2 @@
 # http://crbug.com/241856
 VideoSchedulerTest.StartAndStop
-
-# crbug.com/458691
-ClientSessionTest.ClipboardStubFilter
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index 67243d1..9ea9570 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -1245,14 +1245,6 @@
    obj:*
 }
 {
-   bug_99307
-   Memcheck:Uninitialized
-   fun:modp_b64_encode
-   fun:_ZN4base12Base64Encode*
-   fun:_ZN11web_ui_util15GetImageDataUrlERK8SkBitmap
-   fun:_ZN12_GLOBAL__N_121NetworkInfoDictionary8set_iconERK8SkBitmap
-}
-{
    bug_100982
    Memcheck:Leak
    fun:_Znw*
@@ -3653,3 +3645,4 @@
    fun:_ZN4base12_GLOBAL__N_112WorkerThread10ThreadMainEv
    fun:_ZN4base12_GLOBAL__N_110ThreadFuncEPv
 }
+
diff --git a/ui/events/gestures/gesture_provider_impl.cc b/ui/events/gestures/gesture_provider_impl.cc
index 561273e..58862d1 100644
--- a/ui/events/gestures/gesture_provider_impl.cc
+++ b/ui/events/gestures/gesture_provider_impl.cc
@@ -91,7 +91,7 @@
                                    ui::INPUT_EVENT_LATENCY_UI_COMPONENT);
   gesture_latency->CopyLatencyFrom(
       last_touch_event_latency_info_,
-      ui::INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT);
+      ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT);
 
   if (!handling_event_) {
     // Dispatching event caused by timer.
diff --git a/ui/events/latency_info.cc b/ui/events/latency_info.cc
index 9d16b29..02586d6 100644
--- a/ui/events/latency_info.cc
+++ b/ui/events/latency_info.cc
@@ -20,17 +20,19 @@
     CASE_TYPE(INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_BEGIN_PLUGIN_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT);
-    CASE_TYPE(INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_UI_COMPONENT);
-    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT);
-    CASE_TYPE(INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT);
     CASE_TYPE(WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT);
     CASE_TYPE(WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT);
-    CASE_TYPE(INPUT_EVENT_BROWSER_COMPOSITE_COMPONENT);
-    CASE_TYPE(INPUT_EVENT_BROWSER_SWAP_BUFFER_COMPONENT);
+    CASE_TYPE(TAB_SHOW_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT);
     CASE_TYPE(INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT);
@@ -71,7 +73,8 @@
 }
 
 // This class is for converting latency info to trace buffer friendly format.
-class LatencyInfoTracedValue : public base::trace_event::ConvertableToTraceFormat {
+class LatencyInfoTracedValue
+    : public base::trace_event::ConvertableToTraceFormat {
  public:
   static scoped_refptr<ConvertableToTraceFormat> FromValue(
       scoped_ptr<base::Value> value);
@@ -253,7 +256,7 @@
     }
 
     TRACE_EVENT_FLOW_BEGIN0(
-        "input", "LatencyInfo.Flow", TRACE_ID_DONT_MANGLE(trace_id));
+        "input,benchmark", "LatencyInfo.Flow", TRACE_ID_DONT_MANGLE(trace_id));
   }
 
   LatencyMap::key_type key = std::make_pair(component, id);
@@ -288,7 +291,7 @@
     }
 
     TRACE_EVENT_FLOW_END0(
-        "input", "LatencyInfo.Flow", TRACE_ID_DONT_MANGLE(trace_id));
+        "input,benchmark", "LatencyInfo.Flow", TRACE_ID_DONT_MANGLE(trace_id));
   }
 }
 
diff --git a/ui/events/latency_info.h b/ui/events/latency_info.h
index 1a18f9b..b6724b8 100644
--- a/ui/events/latency_info.h
+++ b/ui/events/latency_info.h
@@ -25,26 +25,25 @@
   // Timestamp when a scroll update for the main thread is begun.
   INPUT_EVENT_LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT,
   // ---------------------------NORMAL COMPONENT-------------------------------
-  // Timestamp when the scroll update gesture event is sent from RWH to
-  // renderer. In Aura, touch event's LatencyInfo is carried over to the gesture
-  // event. So gesture event's INPUT_EVENT_LATENCY_RWH_COMPONENT is the
-  // timestamp when its original touch events is sent from RWH to renderer.
-  // In non-aura platform, INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT
-  // is the same as INPUT_EVENT_LATENCY_RWH_COMPONENT.
-  INPUT_EVENT_LATENCY_SCROLL_UPDATE_RWH_COMPONENT,
   // The original timestamp of the touch event which converts to scroll update.
   INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+  // The original timestamp of the touch event which converts to the *first*
+  // scroll update in a scroll gesture sequence.
+  INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
   // Original timestamp for input event (e.g. timestamp from kernel).
   INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
   // Timestamp when the UI event is created.
   INPUT_EVENT_LATENCY_UI_COMPONENT,
   // This is special component indicating there is rendering scheduled for
-  // the event associated with this LatencyInfo.
-  INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT,
+  // the event associated with this LatencyInfo on main thread.
+  INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT,
+  // This is special component indicating there is rendering scheduled for
+  // the event associated with this LatencyInfo on impl thread.
+  INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT,
   // Timestamp when a scroll update is forwarded to the main thread.
   INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
-  // Timestamp when the touch event is acked.
-  INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT,
+  // Timestamp when the event's ack is received by the RWH.
+  INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT,
   // Frame number when a window snapshot was requested. The snapshot
   // is taken when the rendering results actually reach the screen.
   WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
@@ -54,12 +53,13 @@
   WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT,
   // Timestamp when a tab is requested to be shown.
   TAB_SHOW_COMPONENT,
-  // Timestamp of when the Browser process began compositing
-  INPUT_EVENT_BROWSER_COMPOSITE_COMPONENT,
-  // Timestamp of when the Browser process began swap buffers
-  INPUT_EVENT_BROWSER_SWAP_BUFFER_COMPONENT,
+  // Timestamp when the frame is swapped in renderer.
+  INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
+  // Timestamp of when the browser process receives a buffer swap notification
+  // from the renderer.
+  INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT,
   // Timestamp of when the gpu service began swap buffers, unlike
-  // INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT which measure after
+  // INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT which measures after.
   INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT,
   // ---------------------------TERMINAL COMPONENT-----------------------------
   // TERMINAL COMPONENT is when we show the latency end in chrome://tracing.
@@ -111,7 +111,7 @@
   };
 
   // Empirically determined constant based on a typical scroll sequence.
-  enum { kTypicalMaxComponentsPerLatencyInfo = 9 };
+  enum { kTypicalMaxComponentsPerLatencyInfo = 10 };
 
   enum { kMaxInputCoordinates = 2 };
 
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 639ec7e..3b91dd3 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -562,6 +562,9 @@
 { 'return_type': 'void',
   'names': ['glGetProgramiv'],
   'arguments': 'GLuint program, GLenum pname, GLint* params', },
+{ 'return_type': 'GLint',
+  'names': ['glGetProgramResourceLocation'],
+  'arguments': 'GLuint program, GLenum programInterface, const char* name', },
 { 'return_type': 'void',
   'versions': [{ 'name': 'glGetQueryiv' }],
   'arguments': 'GLenum target, GLenum pname, GLint* params', },
@@ -621,6 +624,9 @@
 { 'return_type': 'const GLubyte*',
   'names': ['glGetString'],
   'arguments': 'GLenum name', },
+{ 'return_type': 'const GLubyte*',
+  'names': ['glGetStringi'],
+  'arguments': 'GLenum name, GLuint index', },
 { 'return_type': 'void',
   'versions': [{ 'name': 'glGetSynciv',
                  'extensions': ['GL_ARB_sync'] }],
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index c886a23..9b0366c 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -343,6 +343,9 @@
                            GLsizei* length,
                            char* infolog) override;
 void glGetProgramivFn(GLuint program, GLenum pname, GLint* params) override;
+GLint glGetProgramResourceLocationFn(GLuint program,
+                                     GLenum programInterface,
+                                     const char* name) override;
 void glGetQueryivFn(GLenum target, GLenum pname, GLint* params) override;
 void glGetQueryivARBFn(GLenum target, GLenum pname, GLint* params) override;
 void glGetQueryObjecti64vFn(GLuint id, GLenum pname, GLint64* params) override;
@@ -376,6 +379,7 @@
                          GLsizei* length,
                          char* source) override;
 const GLubyte* glGetStringFn(GLenum name) override;
+const GLubyte* glGetStringiFn(GLenum name, GLuint index) override;
 void glGetSyncivFn(GLsync sync,
                    GLenum pname,
                    GLsizei bufSize,
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index f1100e8..a655717 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -212,6 +212,7 @@
       GetGLProcAddress("glGetProgramInfoLog"));
   fn.glGetProgramivFn =
       reinterpret_cast<glGetProgramivProc>(GetGLProcAddress("glGetProgramiv"));
+  fn.glGetProgramResourceLocationFn = 0;
   fn.glGetQueryivFn = 0;
   fn.glGetQueryivARBFn = 0;
   fn.glGetQueryObjecti64vFn = 0;
@@ -232,6 +233,7 @@
       GetGLProcAddress("glGetShaderSource"));
   fn.glGetStringFn =
       reinterpret_cast<glGetStringProc>(GetGLProcAddress("glGetString"));
+  fn.glGetStringiFn = 0;
   fn.glGetSyncivFn = 0;
   fn.glGetTexLevelParameterfvFn = 0;
   fn.glGetTexLevelParameterivFn = 0;
@@ -1272,6 +1274,14 @@
     DCHECK(fn.glGetProgramBinaryFn);
   }
 
+  debug_fn.glGetProgramResourceLocationFn = 0;
+  if (ver->IsAtLeastGL(4u, 3u) || ver->IsAtLeastGLES(3u, 1u)) {
+    fn.glGetProgramResourceLocationFn =
+        reinterpret_cast<glGetProgramResourceLocationProc>(
+            GetGLProcAddress("glGetProgramResourceLocation"));
+    DCHECK(fn.glGetProgramResourceLocationFn);
+  }
+
   debug_fn.glGetQueryivFn = 0;
   if (!ver->is_es || ver->IsAtLeastGLES(3u, 0u)) {
     fn.glGetQueryivFn =
@@ -1387,6 +1397,13 @@
     DCHECK(fn.glGetShaderPrecisionFormatFn);
   }
 
+  debug_fn.glGetStringiFn = 0;
+  if (ver->IsAtLeastGL(3u, 0u) || ver->IsAtLeastGLES(3u, 0u)) {
+    fn.glGetStringiFn =
+        reinterpret_cast<glGetStringiProc>(GetGLProcAddress("glGetStringi"));
+    DCHECK(fn.glGetStringiFn);
+  }
+
   debug_fn.glGetSyncivFn = 0;
   if (ver->IsAtLeastGL(3u, 2u) || ver->IsAtLeastGLES(3u, 0u) ||
       ext.b_GL_ARB_sync) {
@@ -3246,6 +3263,20 @@
   g_driver_gl.debug_fn.glGetProgramivFn(program, pname, params);
 }
 
+static GLint GL_BINDING_CALL
+Debug_glGetProgramResourceLocation(GLuint program,
+                                   GLenum programInterface,
+                                   const char* name) {
+  GL_SERVICE_LOG("glGetProgramResourceLocation"
+                 << "(" << program << ", "
+                 << GLEnums::GetStringEnum(programInterface) << ", " << name
+                 << ")");
+  GLint result = g_driver_gl.debug_fn.glGetProgramResourceLocationFn(
+      program, programInterface, name);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 static void GL_BINDING_CALL
 Debug_glGetQueryiv(GLenum target, GLenum pname, GLint* params) {
   GL_SERVICE_LOG("glGetQueryiv"
@@ -3391,6 +3422,16 @@
   return result;
 }
 
+static const GLubyte* GL_BINDING_CALL
+Debug_glGetStringi(GLenum name, GLuint index) {
+  GL_SERVICE_LOG("glGetStringi"
+                 << "(" << GLEnums::GetStringEnum(name) << ", " << index
+                 << ")");
+  const GLubyte* result = g_driver_gl.debug_fn.glGetStringiFn(name, index);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 static void GL_BINDING_CALL Debug_glGetSynciv(GLsync sync,
                                               GLenum pname,
                                               GLsizei bufSize,
@@ -5243,6 +5284,10 @@
     debug_fn.glGetProgramivFn = fn.glGetProgramivFn;
     fn.glGetProgramivFn = Debug_glGetProgramiv;
   }
+  if (!debug_fn.glGetProgramResourceLocationFn) {
+    debug_fn.glGetProgramResourceLocationFn = fn.glGetProgramResourceLocationFn;
+    fn.glGetProgramResourceLocationFn = Debug_glGetProgramResourceLocation;
+  }
   if (!debug_fn.glGetQueryivFn) {
     debug_fn.glGetQueryivFn = fn.glGetQueryivFn;
     fn.glGetQueryivFn = Debug_glGetQueryiv;
@@ -5309,6 +5354,10 @@
     debug_fn.glGetStringFn = fn.glGetStringFn;
     fn.glGetStringFn = Debug_glGetString;
   }
+  if (!debug_fn.glGetStringiFn) {
+    debug_fn.glGetStringiFn = fn.glGetStringiFn;
+    fn.glGetStringiFn = Debug_glGetStringi;
+  }
   if (!debug_fn.glGetSyncivFn) {
     debug_fn.glGetSyncivFn = fn.glGetSyncivFn;
     fn.glGetSyncivFn = Debug_glGetSynciv;
@@ -6652,6 +6701,13 @@
   driver_->fn.glGetProgramivFn(program, pname, params);
 }
 
+GLint GLApiBase::glGetProgramResourceLocationFn(GLuint program,
+                                                GLenum programInterface,
+                                                const char* name) {
+  return driver_->fn.glGetProgramResourceLocationFn(program, programInterface,
+                                                    name);
+}
+
 void GLApiBase::glGetQueryivFn(GLenum target, GLenum pname, GLint* params) {
   driver_->fn.glGetQueryivFn(target, pname, params);
 }
@@ -6740,6 +6796,10 @@
   return driver_->fn.glGetStringFn(name);
 }
 
+const GLubyte* GLApiBase::glGetStringiFn(GLenum name, GLuint index) {
+  return driver_->fn.glGetStringiFn(name, index);
+}
+
 void GLApiBase::glGetSyncivFn(GLsync sync,
                               GLenum pname,
                               GLsizei bufSize,
@@ -8452,6 +8512,15 @@
   gl_api_->glGetProgramivFn(program, pname, params);
 }
 
+GLint TraceGLApi::glGetProgramResourceLocationFn(GLuint program,
+                                                 GLenum programInterface,
+                                                 const char* name) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu",
+                                "TraceGLAPI::glGetProgramResourceLocation")
+  return gl_api_->glGetProgramResourceLocationFn(program, programInterface,
+                                                 name);
+}
+
 void TraceGLApi::glGetQueryivFn(GLenum target, GLenum pname, GLint* params) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glGetQueryiv")
   gl_api_->glGetQueryivFn(target, pname, params);
@@ -8559,6 +8628,11 @@
   return gl_api_->glGetStringFn(name);
 }
 
+const GLubyte* TraceGLApi::glGetStringiFn(GLenum name, GLuint index) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glGetStringi")
+  return gl_api_->glGetStringiFn(name, index);
+}
+
 void TraceGLApi::glGetSyncivFn(GLsync sync,
                                GLenum pname,
                                GLsizei bufSize,
@@ -10539,6 +10613,16 @@
   LOG(ERROR) << "Trying to call glGetProgramiv() without current GL context";
 }
 
+GLint NoContextGLApi::glGetProgramResourceLocationFn(GLuint program,
+                                                     GLenum programInterface,
+                                                     const char* name) {
+  NOTREACHED() << "Trying to call glGetProgramResourceLocation() without "
+                  "current GL context";
+  LOG(ERROR) << "Trying to call glGetProgramResourceLocation() without current "
+                "GL context";
+  return 0;
+}
+
 void NoContextGLApi::glGetQueryivFn(GLenum target,
                                     GLenum pname,
                                     GLint* params) {
@@ -10676,6 +10760,12 @@
   return NULL;
 }
 
+const GLubyte* NoContextGLApi::glGetStringiFn(GLenum name, GLuint index) {
+  NOTREACHED() << "Trying to call glGetStringi() without current GL context";
+  LOG(ERROR) << "Trying to call glGetStringi() without current GL context";
+  return NULL;
+}
+
 void NoContextGLApi::glGetSyncivFn(GLsync sync,
                                    GLenum pname,
                                    GLsizei bufSize,
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index b930484..643a236 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -406,6 +406,10 @@
 typedef void(GL_BINDING_CALL* glGetProgramivProc)(GLuint program,
                                                   GLenum pname,
                                                   GLint* params);
+typedef GLint(GL_BINDING_CALL* glGetProgramResourceLocationProc)(
+    GLuint program,
+    GLenum programInterface,
+    const char* name);
 typedef void(GL_BINDING_CALL* glGetQueryivProc)(GLenum target,
                                                 GLenum pname,
                                                 GLint* params);
@@ -457,6 +461,8 @@
                                                      GLsizei* length,
                                                      char* source);
 typedef const GLubyte*(GL_BINDING_CALL* glGetStringProc)(GLenum name);
+typedef const GLubyte*(GL_BINDING_CALL* glGetStringiProc)(GLenum name,
+                                                          GLuint index);
 typedef void(GL_BINDING_CALL* glGetSyncivProc)(GLsync sync,
                                                GLenum pname,
                                                GLsizei bufSize,
@@ -1072,6 +1078,7 @@
   glGetProgramBinaryProc glGetProgramBinaryFn;
   glGetProgramInfoLogProc glGetProgramInfoLogFn;
   glGetProgramivProc glGetProgramivFn;
+  glGetProgramResourceLocationProc glGetProgramResourceLocationFn;
   glGetQueryivProc glGetQueryivFn;
   glGetQueryivARBProc glGetQueryivARBFn;
   glGetQueryObjecti64vProc glGetQueryObjecti64vFn;
@@ -1088,6 +1095,7 @@
   glGetShaderPrecisionFormatProc glGetShaderPrecisionFormatFn;
   glGetShaderSourceProc glGetShaderSourceFn;
   glGetStringProc glGetStringFn;
+  glGetStringiProc glGetStringiFn;
   glGetSyncivProc glGetSyncivFn;
   glGetTexLevelParameterfvProc glGetTexLevelParameterfvFn;
   glGetTexLevelParameterivProc glGetTexLevelParameterivFn;
@@ -1579,6 +1587,9 @@
   virtual void glGetProgramivFn(GLuint program,
                                 GLenum pname,
                                 GLint* params) = 0;
+  virtual GLint glGetProgramResourceLocationFn(GLuint program,
+                                               GLenum programInterface,
+                                               const char* name) = 0;
   virtual void glGetQueryivFn(GLenum target, GLenum pname, GLint* params) = 0;
   virtual void glGetQueryivARBFn(GLenum target,
                                  GLenum pname,
@@ -1622,6 +1633,7 @@
                                    GLsizei* length,
                                    char* source) = 0;
   virtual const GLubyte* glGetStringFn(GLenum name) = 0;
+  virtual const GLubyte* glGetStringiFn(GLenum name, GLuint index) = 0;
   virtual void glGetSyncivFn(GLsync sync,
                              GLenum pname,
                              GLsizei bufSize,
@@ -2181,6 +2193,8 @@
 #define glGetProgramBinary ::gfx::g_current_gl_context->glGetProgramBinaryFn
 #define glGetProgramInfoLog ::gfx::g_current_gl_context->glGetProgramInfoLogFn
 #define glGetProgramiv ::gfx::g_current_gl_context->glGetProgramivFn
+#define glGetProgramResourceLocation \
+  ::gfx::g_current_gl_context->glGetProgramResourceLocationFn
 #define glGetQueryiv ::gfx::g_current_gl_context->glGetQueryivFn
 #define glGetQueryivARB ::gfx::g_current_gl_context->glGetQueryivARBFn
 #define glGetQueryObjecti64v ::gfx::g_current_gl_context->glGetQueryObjecti64vFn
@@ -2204,6 +2218,7 @@
   ::gfx::g_current_gl_context->glGetShaderPrecisionFormatFn
 #define glGetShaderSource ::gfx::g_current_gl_context->glGetShaderSourceFn
 #define glGetString ::gfx::g_current_gl_context->glGetStringFn
+#define glGetStringi ::gfx::g_current_gl_context->glGetStringiFn
 #define glGetSynciv ::gfx::g_current_gl_context->glGetSyncivFn
 #define glGetTexLevelParameterfv \
   ::gfx::g_current_gl_context->glGetTexLevelParameterfvFn
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index 8d3614b..bf0a857 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -1223,6 +1223,15 @@
   interface_->GetProgramInfoLog(program, bufsize, length, infolog);
 }
 
+GLint GL_BINDING_CALL
+MockGLInterface::Mock_glGetProgramResourceLocation(GLuint program,
+                                                   GLenum programInterface,
+                                                   const char* name) {
+  MakeFunctionUnique("glGetProgramResourceLocation");
+  return interface_->GetProgramResourceLocation(program, programInterface,
+                                                name);
+}
+
 void GL_BINDING_CALL MockGLInterface::Mock_glGetProgramiv(GLuint program,
                                                           GLenum pname,
                                                           GLint* params) {
@@ -1398,6 +1407,12 @@
   return interface_->GetString(name);
 }
 
+const GLubyte* GL_BINDING_CALL
+MockGLInterface::Mock_glGetStringi(GLenum name, GLuint index) {
+  MakeFunctionUnique("glGetStringi");
+  return interface_->GetStringi(name, index);
+}
+
 void GL_BINDING_CALL MockGLInterface::Mock_glGetSynciv(GLsync sync,
                                                        GLenum pname,
                                                        GLsizei bufSize,
@@ -2860,6 +2875,8 @@
     return reinterpret_cast<void*>(Mock_glGetProgramBinaryOES);
   if (strcmp(name, "glGetProgramInfoLog") == 0)
     return reinterpret_cast<void*>(Mock_glGetProgramInfoLog);
+  if (strcmp(name, "glGetProgramResourceLocation") == 0)
+    return reinterpret_cast<void*>(Mock_glGetProgramResourceLocation);
   if (strcmp(name, "glGetProgramiv") == 0)
     return reinterpret_cast<void*>(Mock_glGetProgramiv);
   if (strcmp(name, "glGetQueryObjecti64v") == 0)
@@ -2906,6 +2923,8 @@
     return reinterpret_cast<void*>(Mock_glGetShaderiv);
   if (strcmp(name, "glGetString") == 0)
     return reinterpret_cast<void*>(Mock_glGetString);
+  if (strcmp(name, "glGetStringi") == 0)
+    return reinterpret_cast<void*>(Mock_glGetStringi);
   if (strcmp(name, "glGetSynciv") == 0)
     return reinterpret_cast<void*>(Mock_glGetSynciv);
   if (strcmp(name, "glGetTexLevelParameterfv") == 0)
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index 52abdae..54c8993 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -441,6 +441,10 @@
                                                      GLsizei bufsize,
                                                      GLsizei* length,
                                                      char* infolog);
+static GLint GL_BINDING_CALL
+Mock_glGetProgramResourceLocation(GLuint program,
+                                  GLenum programInterface,
+                                  const char* name);
 static void GL_BINDING_CALL
 Mock_glGetProgramiv(GLuint program, GLenum pname, GLint* params);
 static void GL_BINDING_CALL
@@ -494,6 +498,8 @@
 static void GL_BINDING_CALL
 Mock_glGetShaderiv(GLuint shader, GLenum pname, GLint* params);
 static const GLubyte* GL_BINDING_CALL Mock_glGetString(GLenum name);
+static const GLubyte* GL_BINDING_CALL
+Mock_glGetStringi(GLenum name, GLuint index);
 static void GL_BINDING_CALL Mock_glGetSynciv(GLsync sync,
                                              GLenum pname,
                                              GLsizei bufSize,
diff --git a/ui/gl/gl_bindings_skia_in_process.cc b/ui/gl/gl_bindings_skia_in_process.cc
index 91d0b0f..25dd698 100644
--- a/ui/gl/gl_bindings_skia_in_process.cc
+++ b/ui/gl/gl_bindings_skia_in_process.cc
@@ -229,6 +229,11 @@
   glFlush();
 }
 
+GLvoid StubGLFlushMappedBufferRange(GLenum target, GLintptr offset,
+                                    GLsizeiptr length) {
+  glFlushMappedBufferRange(target, offset, length);
+}
+
 GLvoid StubGLFramebufferRenderbuffer(GLenum target, GLenum attachment,
                                      GLenum renderbuffertarget,
                                      GLuint renderbuffer) {
@@ -331,6 +336,10 @@
   return glGetString(name);
 }
 
+const GLubyte* StubGLGetStringi(GLenum name, GLuint index) {
+  return glGetStringi(name, index);
+}
+
 GLvoid StubGLGetQueryiv(GLenum target, GLenum pname, GLint* params) {
   glGetQueryiv(target, pname, params);
 }
@@ -364,6 +373,20 @@
   glInsertEventMarkerEXT(length, marker);
 }
 
+GLvoid StubGLInvalidateFramebuffer(GLenum target, GLsizei numAttachments,
+                                   const GLenum* attachments) {
+  glInvalidateFramebuffer(target, numAttachments, attachments);
+}
+
+GLvoid StubGLInvalidateSubFramebuffer(GLenum target,
+                                      GLsizei numAttachments,
+                                      const GLenum* attachments,
+                                      GLint x, GLint y,
+                                      GLsizei width, GLsizei height) {
+  glInvalidateSubFramebuffer(target, numAttachments, attachments,
+                             x, y, width, height);
+}
+
 GLvoid StubGLLineWidth(GLfloat width) {
   glLineWidth(width);
 }
@@ -376,6 +399,11 @@
   return glMapBuffer(target, access);
 }
 
+void* StubGLMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
+                           GLbitfield access) {
+  return glMapBufferRange(target, offset, length, access);
+}
+
 GLvoid StubGLPixelStorei(GLenum pname, GLint param) {
   glPixelStorei(pname, param);
 }
@@ -589,6 +617,13 @@
 GLvoid StubGLViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
   glViewport(x, y, width, height);
 }
+
+GLint StubGLGetProgramResourceLocation(GLuint program,
+                                       GLenum programInterface,
+                                       const char* name) {
+  return glGetProgramResourceLocation(program, programInterface, name);
+}
+
 }  // extern "C"
 }  // namespace
 
@@ -622,7 +657,7 @@
   interface->fStandard = standard;
   interface->fExtensions.init(standard,
                               StubGLGetString,
-                              NULL,
+                              StubGLGetStringi,
                               StubGLGetIntegerv);
 
   GrGLInterface::Functions* functions = &interface->fFunctions;
@@ -667,6 +702,7 @@
   functions->fEndQuery = StubGLEndQuery;
   functions->fFinish = StubGLFinish;
   functions->fFlush = StubGLFlush;
+  functions->fFlushMappedBufferRange = StubGLFlushMappedBufferRange;
   functions->fFrontFace = StubGLFrontFace;
   functions->fGenBuffers = StubGLGenBuffers;
   functions->fGenQueries = StubGLGenQueries;
@@ -687,11 +723,15 @@
   functions->fGetShaderiv = StubGLGetShaderiv;
   functions->fGetShaderPrecisionFormat = StubGLGetShaderPrecisionFormat;
   functions->fGetString = StubGLGetString;
+  functions->fGetStringi = StubGLGetStringi;
   functions->fGetTexLevelParameteriv = StubGLGetTexLevelParameteriv;
   functions->fGetUniformLocation = StubGLGetUniformLocation;
   functions->fInsertEventMarker = StubGLInsertEventMarker;
+  functions->fInvalidateFramebuffer = StubGLInvalidateFramebuffer;
+  functions->fInvalidateSubFramebuffer = StubGLInvalidateSubFramebuffer;
   functions->fLineWidth = StubGLLineWidth;
   functions->fLinkProgram = StubGLLinkProgram;
+  functions->fMapBufferRange = StubGLMapBufferRange;
   functions->fPixelStorei = StubGLPixelStorei;
   functions->fPopGroupMarker = StubGLPopGroupMarker;
   functions->fPushGroupMarker = StubGLPushGroupMarker;
@@ -754,11 +794,14 @@
   functions->fRenderbufferStorage = StubGLRenderbufferStorage;
   functions->fRenderbufferStorageMultisample =
     StubGLRenderbufferStorageMultisample;
+  functions->fRenderbufferStorageMultisampleES2EXT =
+    StubGLRenderbufferStorageMultisample;
   functions->fBlitFramebuffer = StubGLBlitFramebuffer;
   functions->fMapBuffer = StubGLMapBuffer;
   functions->fUnmapBuffer = StubGLUnmapBuffer;
   functions->fBindFragDataLocationIndexed =
     StubGLBindFragDataLocationIndexed;
+  functions->fGetProgramResourceLocation = StubGLGetProgramResourceLocation;
 
   return interface;
 }
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index f89f1d2..720e8e2 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -106,8 +106,8 @@
   if(!version_info_) {
     std::string version = GetGLVersion();
     std::string renderer = GetGLRenderer();
-    version_info_ = scoped_ptr<GLVersionInfo>(
-        new GLVersionInfo(version.c_str(), renderer.c_str()));
+    version_info_ =
+        make_scoped_ptr(new GLVersionInfo(version.c_str(), renderer.c_str()));
   }
   return version_info_.get();
 }
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index d4bc780..102407c 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -122,12 +122,15 @@
 
 void GLContextCGL::Destroy() {
   if (discrete_pixelformat_) {
-    // Delay releasing the pixel format for 10 seconds to reduce the number of
-    // unnecessary GPU switches.
-    base::MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&CGLReleasePixelFormat, discrete_pixelformat_),
-        base::TimeDelta::FromSeconds(10));
+    if (base::MessageLoop::current() != NULL) {
+      // Delay releasing the pixel format for 10 seconds to reduce the number of
+      // unnecessary GPU switches.
+      base::MessageLoop::current()->PostDelayedTask(
+          FROM_HERE, base::Bind(&CGLReleasePixelFormat, discrete_pixelformat_),
+          base::TimeDelta::FromSeconds(10));
+    } else {
+      CGLReleasePixelFormat(discrete_pixelformat_);
+    }
     discrete_pixelformat_ = NULL;
   }
   if (context_) {
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index 5b47058..7bec019 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -346,6 +346,8 @@
     GetProgramInfoLog,
     void(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog));
 MOCK_METHOD3(GetProgramiv, void(GLuint program, GLenum pname, GLint* params));
+MOCK_METHOD3(GetProgramResourceLocation,
+             GLint(GLuint program, GLenum programInterface, const char* name));
 MOCK_METHOD3(GetQueryiv, void(GLenum target, GLenum pname, GLint* params));
 MOCK_METHOD3(GetQueryivARB, void(GLenum target, GLenum pname, GLint* params));
 MOCK_METHOD3(GetQueryObjecti64v,
@@ -376,6 +378,7 @@
     GetShaderSource,
     void(GLuint shader, GLsizei bufsize, GLsizei* length, char* source));
 MOCK_METHOD1(GetString, const GLubyte*(GLenum name));
+MOCK_METHOD2(GetStringi, const GLubyte*(GLenum name, GLuint index));
 MOCK_METHOD5(GetSynciv,
              void(GLsync sync,
                   GLenum pname,
diff --git a/url/BUILD.gn b/url/BUILD.gn
index 03e90cb..964a08b 100644
--- a/url/BUILD.gn
+++ b/url/BUILD.gn
@@ -58,11 +58,11 @@
 
   defines = [ "URL_IMPLEMENTATION" ]
 
-  configs += [ ":url_icu_config" ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
+  configs += [
+    ":url_icu_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   deps = [
     "//base",
@@ -101,19 +101,15 @@
       "url_util_unittest.cc",
     ]
 
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     #if (is_posix && !is_mac && !is_ios) {
     #  if (use_allocator!="none") {
     #    deps += "//base/allocator"
     #  }
     #}
 
-    if (is_win) {
-      cflags = [
-        # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-        "/wd4267",
-      ]
-    }
-
     deps = [
       ":url",
       "//base",
diff --git a/url/url_canon.h b/url/url_canon.h
index 89e3509..432f291 100644
--- a/url/url_canon.h
+++ b/url/url_canon.h
@@ -734,8 +734,8 @@
   // Returns a pointer to a static empty string that is used as a placeholder
   // to indicate a component should be deleted (see below).
   const CHAR* Placeholder() {
-    static const CHAR empty_string = 0;
-    return &empty_string;
+    static const CHAR empty_cstr = 0;
+    return &empty_cstr;
   }
 
   // We support three states: