Chromium -> Mojo roll.

Update from https://crrev.com/321900

Changes:
(1) OpenTypeSanitizer.cpp: Removed use of ots::EnableWOFF2() which was
removed from library and implementation was empty anyways.
(2) GraphicsContext.cpp: Header location for SkMatrixImageFilter.h moved.
(3) SkiaImageFilterBuilder.cpp: Header location for SkMatrixImageFilter.h moved.
(4) FEDropShadow.cpp: Function signature changed.

R=davemoore@chromium.org, jamesr@chromium.org
BUG=453591

Review URL: https://codereview.chromium.org/1028333002
diff --git a/DEPS b/DEPS
index 8280b31..c3abc80 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': 'cdeca446197329de91d87d12ad689d03d7e4d261',
+  'skia_revision': '92d04da38f03dfabd8cd9a7244588a49be9a2f41',
   # 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': '2a8ff45e5c7fce5037cf975c4997718c324744dd',
+  'v8_revision': '230d131d173ab2d60291d303177bc04ec3f6e519',
   # 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': '524e3bde19d0df76cc9d1c5926345f5d0ec4d3f8',
+  'angle_revision': 'bdd419f9f5b006e913606e7363125942c8ae06bc',
   # 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': 'd4dd4f79f60bf019625b3a1436979b0a42c892df',
+  'buildtools_revision': '3b302fef93f7cc58d9b8168466905237484b2772',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Dart
   # and whatever else without interference from each other.
@@ -41,11 +41,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '28ddd48bfd84c55cc51d0b16fa533c51affdeb5b',
+  'pdfium_revision': 'b0115665b0f33971f1b7077740d51e155583cec0',
   # 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': 'bf0df92964565f819881e78ff3bfb5343e95e130',
+  'boringssl_revision': '642f1498d056dbba3e50ed5a232ab2f482626dec',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lss
   # and whatever else without interference from each other.
@@ -79,7 +79,7 @@
    Var('chromium_git') + '/angle/angle.git' + '@' +  Var('angle_revision'),
 
   'src/third_party/icu':
-   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'eda9e75b1fa17f57ffa369ee3543a2301b68d0a9',
+   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '7c81740601355556e630da515b74d889ba2f8d08',
 
   'src/tools/grit':
     Var('chromium_git') + '/external/grit-i18n.git' + '@' + '0287c187b11ed53590254e4d817e836a44a7a1a7', # from svn revision 186
@@ -150,7 +150,7 @@
         Var('chromium_git') + '/external/jsr-305.git' + '@' + '642c508235471f7220af6d5df2d3210e3bfc0919',
 
     'src/third_party/android_tools':
-     Var('chromium_git') + '/android_tools.git' + '@' + '98a434576d8d9a65f9bdb8ea161e158142c0c5e5',
+     Var('chromium_git') + '/android_tools.git' + '@' + 'a1ffd63322c438627d78ea56eb73fb8779e06950',
 
     'src/third_party/appurify-python/src':
      Var('chromium_git') + '/external/github.com/appurify/appurify-python.git' + '@' + 'ee7abd5c5ae3106f72b2a0b9d2cb55094688e867',
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f103446..8b9b5bf 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -370,6 +370,10 @@
     "profiler/scoped_profile.h",
     "profiler/scoped_tracker.cc",
     "profiler/scoped_tracker.h",
+    "profiler/stack_sampling_profiler.cc",
+    "profiler/stack_sampling_profiler.h",
+    "profiler/stack_sampling_profiler_posix.cc",
+    "profiler/stack_sampling_profiler_win.cc",
     "profiler/tracked_time.cc",
     "profiler/tracked_time.h",
     "rand_util.cc",
@@ -484,6 +488,8 @@
     "threading/non_thread_safe_impl.h",
     "threading/platform_thread.h",
     "threading/platform_thread_android.cc",
+    "threading/platform_thread_internal_posix.cc",
+    "threading/platform_thread_internal_posix.h",
     "threading/platform_thread_linux.cc",
     "threading/platform_thread_mac.mm",
     "threading/platform_thread_posix.cc",
@@ -740,6 +746,7 @@
     sources -= [
       "native_library_posix.cc",
       "strings/sys_string_conversions_posix.cc",
+      "threading/platform_thread_internal_posix.cc",
     ]
   } else {
     # Non-Mac.
@@ -1188,6 +1195,7 @@
     "process/process_unittest.cc",
     "process/process_util_unittest.cc",
     "process/process_util_unittest_ios.cc",
+    "profiler/stack_sampling_profiler_unittest.cc",
     "profiler/tracked_time_unittest.cc",
     "rand_util_unittest.cc",
     "scoped_clear_errno_unittest.cc",
@@ -1248,15 +1256,6 @@
     "timer/mock_timer_unittest.cc",
     "timer/timer_unittest.cc",
     "tools_sanity_unittest.cc",
-    "trace_event/memory_dump_manager_unittest.cc",
-    "trace_event/process_memory_maps_dump_provider_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",
-    "trace_event/trace_event_system_stats_monitor_unittest.cc",
-    "trace_event/trace_event_unittest.cc",
-    "trace_event/trace_event_win_unittest.cc",
     "tracked_objects_unittest.cc",
     "tuple_unittest.cc",
     "values_unittest.cc",
@@ -1294,6 +1293,7 @@
     "//base/test:test_support",
     "//base/third_party/dynamic_annotations",
     "//base/third_party/nspr",
+    "//base/trace_event:trace_event_unittests",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/icu",
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index 4198853..71b79b1 100644
--- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -14,6 +14,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
@@ -480,4 +482,25 @@
             window.setStatusBarColor(statusBarColor);
         }
     }
+
+    /**
+     * @see android.content.res.Resources#getDrawable(int id).
+     */
+    @SuppressWarnings("deprecation")
+    public static Drawable getDrawable(Resources res, int id) throws NotFoundException {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            return res.getDrawable(id, null);
+        } else {
+            return res.getDrawable(id);
+        }
+    }
+
+    /**
+     * @see android.view.View#announceForAccessibility(CharSequence text)
+     */
+    public static void announceForAccessibility(View view, CharSequence text) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            view.announceForAccessibility(text);
+        }
+    }
 }
diff --git a/base/android/java/src/org/chromium/base/CommandLine.java b/base/android/java/src/org/chromium/base/CommandLine.java
index 43e0004..9f54079 100644
--- a/base/android/java/src/org/chromium/base/CommandLine.java
+++ b/base/android/java/src/org/chromium/base/CommandLine.java
@@ -306,8 +306,9 @@
 
             // Append the switch and update the switches/arguments divider mArgsBegin.
             String combinedSwitchString = SWITCH_PREFIX + switchString;
-            if (value != null && !value.isEmpty())
+            if (value != null && !value.isEmpty()) {
                 combinedSwitchString += SWITCH_VALUE_SEPARATOR + value;
+            }
 
             mArgs.add(mArgsBegin++, combinedSwitchString);
         }
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 bbf76cb..dbeb758 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
@@ -237,9 +237,9 @@
                     System.loadLibrary(TAG + ".cr");
                 }
                 sRelroSharingSupported = nativeCanUseSharedRelro();
-                if (!sRelroSharingSupported)
+                if (!sRelroSharingSupported) {
                     Log.w(TAG, "This system cannot safely share RELRO sections");
-                else {
+                } else {
                     if (DEBUG) Log.i(TAG, "This system supports safe shared RELRO sections");
                 }
 
@@ -255,8 +255,9 @@
                     case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY:
                         sBrowserUsesSharedRelro =
                                 (sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW);
-                        if (sBrowserUsesSharedRelro)
+                        if (sBrowserUsesSharedRelro) {
                             Log.w(TAG, "Low-memory device: shared RELROs used in all processes");
+                        }
                         break;
                     case BROWSER_SHARED_RELRO_CONFIG_ALWAYS:
                         Log.w(TAG, "Beware: shared RELROs used in all processes!");
@@ -345,10 +346,11 @@
             assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
                    || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL;
             if (DEBUG) {
-                if (memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW)
+                if (memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
                     Log.i(TAG, "Simulating a low-memory device");
-                else
+                } else {
                     Log.i(TAG, "Simulating a regular-memory device");
+                }
             }
             sMemoryDeviceConfig = memoryDeviceConfig;
         }
@@ -363,8 +365,7 @@
         // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
         // defined as 1 will use this linker. For all others (the default), the
         // auto-generated NativeLibraries.sUseLinker variable will be false.
-        if (!NativeLibraries.sUseLinker)
-            return false;
+        if (!NativeLibraries.sUseLinker) return false;
 
         synchronized (Linker.class) {
             ensureInitializedLocked();
@@ -693,8 +694,7 @@
         }
 
         // In service processes, close all file descriptors from the map now.
-        if (!sInBrowserProcess)
-            closeLibInfoMap(relroMap);
+        if (!sInBrowserProcess) closeLibInfoMap(relroMap);
 
         if (DEBUG) Log.i(TAG, "Linker.useSharedRelrosLocked() exiting");
     }
diff --git a/base/base.gyp b/base/base.gyp
index c7a2481..ac37a89 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -595,6 +595,7 @@
         'process/process_metrics_unittest_ios.cc',
         'process/process_unittest.cc',
         'process/process_util_unittest.cc',
+        'profiler/stack_sampling_profiler_unittest.cc',
         'profiler/tracked_time_unittest.cc',
         'rand_util_unittest.cc',
         'scoped_clear_errno_unittest.cc',
@@ -656,15 +657,6 @@
         'timer/mock_timer_unittest.cc',
         'timer/timer_unittest.cc',
         'tools_sanity_unittest.cc',
-        'trace_event/memory_dump_manager_unittest.cc',
-        'trace_event/process_memory_maps_dump_provider_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',
-        'trace_event/trace_event_system_stats_monitor_unittest.cc',
-        'trace_event/trace_event_unittest.cc',
-        'trace_event/trace_event_win_unittest.cc',
         'tracked_objects_unittest.cc',
         'tuple_unittest.cc',
         'values_unittest.cc',
@@ -689,6 +681,7 @@
         'win/startup_information_unittest.cc',
         'win/win_util_unittest.cc',
         'win/wrapped_window_proc_unittest.cc',
+        '<@(trace_event_test_sources)',
       ],
       'dependencies': [
         'base',
@@ -975,8 +968,8 @@
         'test/simple_test_tick_clock.h',
         'test/task_runner_test_template.cc',
         'test/task_runner_test_template.h',
-        'test/test_discardable_memory_shmem_allocator.cc',
-        'test/test_discardable_memory_shmem_allocator.h',
+        'test/test_discardable_memory_allocator.cc',
+        'test/test_discardable_memory_allocator.h',
         'test/test_file_util.cc',
         'test/test_file_util.h',
         'test/test_file_util_android.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 13cba85..825bca1 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 {
+  'includes': [
+    'trace_event/trace_event.gypi',
+  ],
   'target_defaults': {
     'variables': {
       'base_target': 0,
@@ -318,10 +321,8 @@
           'memory/aligned_memory.h',
           'memory/discardable_memory.cc',
           'memory/discardable_memory.h',
-          'memory/discardable_memory_shmem.cc',
-          'memory/discardable_memory_shmem.h',
-          'memory/discardable_memory_shmem_allocator.cc',
-          'memory/discardable_memory_shmem_allocator.h',
+          'memory/discardable_memory_allocator.cc',
+          'memory/discardable_memory_allocator.h',
           'memory/discardable_shared_memory.cc',
           'memory/discardable_shared_memory.h',
           'memory/linked_ptr.h',
@@ -487,6 +488,10 @@
           'profiler/scoped_profile.h',
           'profiler/scoped_tracker.cc',
           'profiler/scoped_tracker.h',
+          'profiler/stack_sampling_profiler.cc',
+          'profiler/stack_sampling_profiler.h',
+          'profiler/stack_sampling_profiler_posix.cc',
+          'profiler/stack_sampling_profiler_win.cc',
           'profiler/tracked_time.cc',
           'profiler/tracked_time.h',
           'rand_util.cc',
@@ -602,6 +607,8 @@
           'threading/non_thread_safe_impl.h',
           'threading/platform_thread.h',
           'threading/platform_thread_android.cc',
+          'threading/platform_thread_internal_posix.cc',
+          'threading/platform_thread_internal_posix.h',
           'threading/platform_thread_linux.cc',
           'threading/platform_thread_mac.mm',
           'threading/platform_thread_posix.cc',
@@ -660,34 +667,6 @@
           'timer/mock_timer.h',
           'timer/timer.cc',
           'timer/timer.h',
-          'trace_event/memory_dump_manager.cc',
-          'trace_event/memory_dump_manager.h',
-          'trace_event/memory_dump_provider.h',
-          'trace_event/process_memory_dump.cc',
-          'trace_event/process_memory_dump.h',
-          'trace_event/process_memory_maps.cc',
-          'trace_event/process_memory_maps.h',
-          'trace_event/process_memory_maps_dump_provider.cc',
-          'trace_event/process_memory_maps_dump_provider.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',
-          'trace_event/trace_event_argument.h',
-          'trace_event/trace_event_impl.cc',
-          'trace_event/trace_event_impl.h',
-          'trace_event/trace_event_impl_constants.cc',
-          'trace_event/trace_event_memory.cc',
-          'trace_event/trace_event_memory.h',
-          'trace_event/trace_event_synthetic_delay.cc',
-          'trace_event/trace_event_synthetic_delay.h',
-          'trace_event/trace_event_system_stats_monitor.cc',
-          'trace_event/trace_event_system_stats_monitor.h',
-          'trace_event/trace_event_win.cc',
-          'trace_event/trace_event_win.h',
           'tracked_objects.cc',
           'tracked_objects.h',
           'tracking_info.cc',
@@ -750,6 +729,7 @@
           'win/windows_version.h',
           'win/wrapped_window_proc.cc',
           'win/wrapped_window_proc.h',
+          '<@(trace_event_sources)',
         ],
         'defines': [
           'BASE_IMPLEMENTATION',
@@ -896,6 +876,7 @@
               ['include', '^process/.*_ios\.(cc|mm)$'],
               ['include', '^process/memory_stubs\.cc$'],
               ['include', '^process/process_handle_posix\.cc$'],
+              ['exclude', '^threading/platform_thread_internal_posix\\.(h|cc)'],
               ['exclude', 'files/file_path_watcher_fsevents.cc'],
               ['exclude', 'files/file_path_watcher_fsevents.h'],
               ['include', 'files/file_path_watcher_mac.cc'],
@@ -963,10 +944,11 @@
           }],
           ['(OS == "mac" or OS == "ios") and >(nacl_untrusted_build)==0', {
             'sources/': [
-              ['exclude', '^files/file_path_watcher_stub\\.cc$'],
               ['exclude', '^base_paths_posix\\.cc$'],
+              ['exclude', '^files/file_path_watcher_stub\\.cc$'],
               ['exclude', '^native_library_posix\\.cc$'],
               ['exclude', '^strings/sys_string_conversions_posix\\.cc$'],
+              ['exclude', '^threading/platform_thread_internal_posix\\.cc$'],
             ],
           }],
           ['<(os_bsd)==1 and >(nacl_untrusted_build)==0', {
diff --git a/base/files/file.h b/base/files/file.h
index 7b6366c..13c8a96 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -128,9 +128,9 @@
 
   // Used to hold information about a given file.
   // If you add more fields to this structure (platform-specific fields are OK),
-  // make sure to update all functions that use it in file_util_{win|posix}.cc
-  // too, and the ParamTraits<base::PlatformFileInfo> implementation in
-  // chrome/common/common_param_traits.cc.
+  // make sure to update all functions that use it in file_util_{win|posix}.cc,
+  // too, and the ParamTraits<base::File::Info> implementation in
+  // ipc/ipc_message_utils.cc.
   struct BASE_EXPORT Info {
     Info();
     ~Info();
@@ -145,7 +145,8 @@
     // True if the file corresponds to a directory.
     bool is_directory;
 
-    // True if the file corresponds to a symbolic link.
+    // True if the file corresponds to a symbolic link.  For Windows currently
+    // not supported and thus always false.
     bool is_symbolic_link;
 
     // The last modified time of a file.
@@ -287,6 +288,13 @@
   // Unlock a file previously locked.
   Error Unlock();
 
+  // Returns a new object referencing this file for use within the current
+  // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File
+  // object that was created or initialized with this flag will have unlinked
+  // the underlying file when it was created or opened. On Windows, the
+  // underlying file is deleted when the last handle to it is closed.
+  File Duplicate();
+
   bool async() const { return async_; }
 
 #if defined(OS_WIN)
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index 663f099..517390f 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -463,6 +463,20 @@
   return CallFctnlFlock(file_.get(), false);
 }
 
+File File::Duplicate() {
+  if (!IsValid())
+    return File();
+
+  PlatformFile other_fd = dup(GetPlatformFile());
+  if (other_fd == -1)
+    return File(OSErrorToFileError(errno));
+
+  File other(other_fd);
+  if (async())
+    other.async_ = true;
+  return other.Pass();
+}
+
 // Static.
 File::Error File::OSErrorToFileError(int saved_errno) {
   switch (saved_errno) {
diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc
index 3bc2db6..5c59424 100644
--- a/base/files/file_unittest.cc
+++ b/base/files/file_unittest.cc
@@ -443,6 +443,49 @@
   EXPECT_EQ(kOffset, file.Seek(base::File::FROM_END, -kOffset));
 }
 
+TEST(FileTest, Duplicate) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath file_path = temp_dir.path().AppendASCII("file");
+  File file(file_path,(base::File::FLAG_CREATE |
+                       base::File::FLAG_READ |
+                       base::File::FLAG_WRITE));
+  ASSERT_TRUE(file.IsValid());
+
+  File file2(file.Duplicate());
+  ASSERT_TRUE(file2.IsValid());
+
+  // Write through one handle, close it, read through the other.
+  static const char kData[] = "now is a good time.";
+  static const int kDataLen = sizeof(kData) - 1;
+
+  ASSERT_EQ(0, file.Seek(base::File::FROM_CURRENT, 0));
+  ASSERT_EQ(0, file2.Seek(base::File::FROM_CURRENT, 0));
+  ASSERT_EQ(kDataLen, file.WriteAtCurrentPos(kData, kDataLen));
+  ASSERT_EQ(kDataLen, file.Seek(base::File::FROM_CURRENT, 0));
+  ASSERT_EQ(kDataLen, file2.Seek(base::File::FROM_CURRENT, 0));
+  file.Close();
+  char buf[kDataLen];
+  ASSERT_EQ(kDataLen, file2.Read(0, &buf[0], kDataLen));
+  ASSERT_EQ(std::string(kData, kDataLen), std::string(&buf[0], kDataLen));
+}
+
+TEST(FileTest, DuplicateDeleteOnClose) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath file_path = temp_dir.path().AppendASCII("file");
+  File file(file_path,(base::File::FLAG_CREATE |
+                       base::File::FLAG_READ |
+                       base::File::FLAG_WRITE |
+                       base::File::FLAG_DELETE_ON_CLOSE));
+  ASSERT_TRUE(file.IsValid());
+  File file2(file.Duplicate());
+  ASSERT_TRUE(file2.IsValid());
+  file.Close();
+  file2.Close();
+  ASSERT_FALSE(base::PathExists(file_path));
+}
+
 #if defined(OS_WIN)
 TEST(FileTest, GetInfoForDirectory) {
   base::ScopedTempDir temp_dir;
diff --git a/base/files/file_win.cc b/base/files/file_win.cc
index 727b5ce..9f3033c 100644
--- a/base/files/file_win.cc
+++ b/base/files/file_win.cc
@@ -309,6 +309,28 @@
   return FILE_OK;
 }
 
+File File::Duplicate() {
+  if (!IsValid())
+    return File();
+
+  HANDLE other_handle = nullptr;
+
+  if (!::DuplicateHandle(GetCurrentProcess(),  // hSourceProcessHandle
+                         GetPlatformFile(),
+                         GetCurrentProcess(),  // hTargetProcessHandle
+                         &other_handle,
+                         0,  // dwDesiredAccess ignored due to SAME_ACCESS
+                         FALSE,  // !bInheritHandle
+                         DUPLICATE_SAME_ACCESS)) {
+    return File(OSErrorToFileError(GetLastError()));
+  }
+
+  File other(other_handle);
+  if (async())
+    other.async_ = true;
+  return other.Pass();
+}
+
 // Static.
 File::Error File::OSErrorToFileError(DWORD last_error) {
   switch (last_error) {
diff --git a/base/memory/BUILD.gn b/base/memory/BUILD.gn
index 3d4c22c..a0608e8 100644
--- a/base/memory/BUILD.gn
+++ b/base/memory/BUILD.gn
@@ -8,10 +8,8 @@
     "aligned_memory.h",
     "discardable_memory.cc",
     "discardable_memory.h",
-    "discardable_memory_shmem.cc",
-    "discardable_memory_shmem.h",
-    "discardable_memory_shmem_allocator.cc",
-    "discardable_memory_shmem_allocator.h",
+    "discardable_memory_allocator.cc",
+    "discardable_memory_allocator.h",
     "discardable_shared_memory.cc",
     "discardable_shared_memory.h",
     "linked_ptr.h",
@@ -42,10 +40,8 @@
     sources -= [
       "discardable_memory.cc",
       "discardable_memory.h",
-      "discardable_memory_shmem.cc",
-      "discardable_memory_shmem.h",
-      "discardable_memory_shmem_allocator.cc",
-      "discardable_memory_shmem_allocator.h",
+      "discardable_memory_allocator.cc",
+      "discardable_memory_allocator.h",
       "discardable_shared_memory.cc",
       "discardable_shared_memory.h",
       "shared_memory_posix.cc",
diff --git a/base/memory/discardable_memory.cc b/base/memory/discardable_memory.cc
index 0e3b58a..d50f185 100644
--- a/base/memory/discardable_memory.cc
+++ b/base/memory/discardable_memory.cc
@@ -4,14 +4,12 @@
 
 #include "base/memory/discardable_memory.h"
 
-#include "base/memory/discardable_memory_shmem.h"
-
 namespace base {
 
-// static
-scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
-    size_t size) {
-  return make_scoped_ptr(new internal::DiscardableMemoryShmem(size));
+DiscardableMemory::DiscardableMemory() {
+}
+
+DiscardableMemory::~DiscardableMemory() {
 }
 
 }  // namespace base
diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h
index ce0f0bb..a0882cc 100644
--- a/base/memory/discardable_memory.h
+++ b/base/memory/discardable_memory.h
@@ -5,26 +5,24 @@
 #ifndef BASE_MEMORY_DISCARDABLE_MEMORY_H_
 #define BASE_MEMORY_DISCARDABLE_MEMORY_H_
 
-#include <string>
-#include <vector>
-
 #include "base/base_export.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
 
 namespace base {
 
-// Platform abstraction for discardable memory. DiscardableMemory is used to
-// cache large objects without worrying about blowing out memory, both on mobile
-// devices where there is no swap, and desktop devices where unused free memory
-// should be used to help the user experience. This is preferable to releasing
-// memory in response to an OOM signal because it is simpler, though it has less
-// flexibility as to which objects get discarded.
+// Discardable memory is used to cache large objects without worrying about
+// blowing out memory, both on mobile devices where there is no swap, and
+// desktop devices where unused free memory should be used to help the user
+// experience. This is preferable to releasing memory in response to an OOM
+// signal because it is simpler and provides system-wide management of
+// purgable memory, though it has less flexibility as to which objects get
+// discarded.
 //
 // Discardable memory has two states: locked and unlocked. While the memory is
-// locked, it will not be discarded. Unlocking the memory allows the OS to
-// reclaim it if needed. Locks do not nest.
+// locked, it will not be discarded. Unlocking the memory allows the
+// discardable memory system and the OS to reclaim it if needed. Locks do not
+// nest.
 //
 // Notes:
 //   - The paging behavior of memory while it is locked is not specified. While
@@ -39,19 +37,10 @@
 //     responsibility of users of discardable memory to ensure there are no
 //     races.
 //
-// References:
-//   - Linux: http://lwn.net/Articles/452035/
-//   - Mac: http://trac.webkit.org/browser/trunk/Source/WebCore/platform/mac/PurgeableBufferMac.cpp
-//          the comment starting with "vm_object_purgable_control" at
-//            http://www.opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/vm/vm_object.c
-//
-// Thread-safety: DiscardableMemory instances are not thread-safe.
 class BASE_EXPORT DiscardableMemory {
  public:
-  virtual ~DiscardableMemory() {}
-
-  // Create a DiscardableMemory instance with |size|.
-  static scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size);
+  DiscardableMemory();
+  virtual ~DiscardableMemory();
 
   // Locks the memory so that it will not be purged by the system. Returns
   // true on success. If the return value is false then this object should be
diff --git a/base/memory/discardable_memory_allocator.cc b/base/memory/discardable_memory_allocator.cc
new file mode 100644
index 0000000..002a3ba
--- /dev/null
+++ b/base/memory/discardable_memory_allocator.cc
@@ -0,0 +1,34 @@
+// 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/memory/discardable_memory_allocator.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace {
+
+DiscardableMemoryAllocator* g_allocator = nullptr;
+
+}  // namespace
+
+// static
+void DiscardableMemoryAllocator::SetInstance(
+    DiscardableMemoryAllocator* allocator) {
+  DCHECK(allocator);
+
+  // Make sure this function is only called once before the first call
+  // to GetInstance().
+  DCHECK(!g_allocator);
+
+  g_allocator = allocator;
+}
+
+// static
+DiscardableMemoryAllocator* DiscardableMemoryAllocator::GetInstance() {
+  DCHECK(g_allocator);
+  return g_allocator;
+}
+
+}  // namespace base
diff --git a/base/memory/discardable_memory_allocator.h b/base/memory/discardable_memory_allocator.h
new file mode 100644
index 0000000..400f87a
--- /dev/null
+++ b/base/memory/discardable_memory_allocator.h
@@ -0,0 +1,32 @@
+// 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_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
+
+#include "base/base_export.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class DiscardableMemory;
+
+class BASE_EXPORT DiscardableMemoryAllocator {
+ public:
+  // Returns the allocator instance.
+  static DiscardableMemoryAllocator* GetInstance();
+
+  // Sets the allocator instance. Can only be called once, e.g. on startup.
+  // Ownership of |instance| remains with the caller.
+  static void SetInstance(DiscardableMemoryAllocator* allocator);
+
+  virtual scoped_ptr<DiscardableMemory> AllocateLockedDiscardableMemory(
+      size_t size) = 0;
+
+ protected:
+  virtual ~DiscardableMemoryAllocator() {}
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/base/memory/discardable_memory_shmem.cc b/base/memory/discardable_memory_shmem.cc
deleted file mode 100644
index 059d84c..0000000
--- a/base/memory/discardable_memory_shmem.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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.
-
-#include "base/memory/discardable_memory_shmem.h"
-
-#include "base/lazy_instance.h"
-#include "base/memory/discardable_memory_shmem_allocator.h"
-
-namespace base {
-namespace internal {
-
-DiscardableMemoryShmem::DiscardableMemoryShmem(size_t bytes)
-    : chunk_(DiscardableMemoryShmemAllocator::GetInstance()
-                 ->AllocateLockedDiscardableMemory(bytes)),
-      is_locked_(true) {
-  DCHECK(chunk_);
-}
-
-DiscardableMemoryShmem::~DiscardableMemoryShmem() {
-  if (is_locked_)
-    Unlock();
-}
-
-bool DiscardableMemoryShmem::Lock() {
-  DCHECK(!is_locked_);
-  DCHECK(chunk_);
-
-  if (!chunk_->Lock()) {
-    chunk_.reset();
-    return false;
-  }
-
-  is_locked_ = true;
-  return true;
-}
-
-void DiscardableMemoryShmem::Unlock() {
-  DCHECK(is_locked_);
-  chunk_->Unlock();
-  is_locked_ = false;
-}
-
-void* DiscardableMemoryShmem::Memory() const {
-  DCHECK(is_locked_);
-  return chunk_->Memory();
-}
-
-}  // namespace internal
-}  // namespace base
diff --git a/base/memory/discardable_memory_shmem.h b/base/memory/discardable_memory_shmem.h
deleted file mode 100644
index f394562..0000000
--- a/base/memory/discardable_memory_shmem.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_H_
-#define BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_H_
-
-#include "base/memory/discardable_memory.h"
-
-namespace base {
-class DiscardableMemoryShmemChunk;
-
-namespace internal {
-
-class DiscardableMemoryShmem : public DiscardableMemory {
- public:
-  explicit DiscardableMemoryShmem(size_t bytes);
-  ~DiscardableMemoryShmem() override;
-
-  bool Initialize();
-
-  // Overridden from DiscardableMemory:
-  bool Lock() override;
-  void Unlock() override;
-  void* Memory() const override;
-
- private:
-  scoped_ptr<DiscardableMemoryShmemChunk> chunk_;
-  bool is_locked_;
-
-  DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmem);
-};
-
-}  // namespace internal
-}  // namespace base
-
-#endif  // BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_H_
diff --git a/base/memory/discardable_memory_shmem_allocator.cc b/base/memory/discardable_memory_shmem_allocator.cc
deleted file mode 100644
index a87c58d..0000000
--- a/base/memory/discardable_memory_shmem_allocator.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-#include "base/memory/discardable_memory_shmem_allocator.h"
-
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/discardable_shared_memory.h"
-
-namespace base {
-namespace {
-
-DiscardableMemoryShmemAllocator* g_allocator = nullptr;
-
-}  // namespace
-
-// static
-void DiscardableMemoryShmemAllocator::SetInstance(
-    DiscardableMemoryShmemAllocator* allocator) {
-  DCHECK(allocator);
-
-  // Make sure this function is only called once before the first call
-  // to GetInstance().
-  DCHECK(!g_allocator);
-
-  g_allocator = allocator;
-}
-
-// static
-DiscardableMemoryShmemAllocator*
-DiscardableMemoryShmemAllocator::GetInstance() {
-  DCHECK(g_allocator);
-  return g_allocator;
-}
-
-}  // namespace base
diff --git a/base/memory/discardable_memory_shmem_allocator.h b/base/memory/discardable_memory_shmem_allocator.h
deleted file mode 100644
index ac4118e..0000000
--- a/base/memory/discardable_memory_shmem_allocator.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.
-
-#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_
-#define BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_
-
-#include "base/base_export.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace base {
-
-// TODO(reveman): Remove this by having allocator interface return
-// real DiscardableMemory instances. crbug.com/442945
-class BASE_EXPORT DiscardableMemoryShmemChunk {
- public:
-  virtual ~DiscardableMemoryShmemChunk() {}
-
-  virtual bool Lock() = 0;
-  virtual void Unlock() = 0;
-  virtual void* Memory() const = 0;
-};
-
-class BASE_EXPORT DiscardableMemoryShmemAllocator {
- public:
-  // Returns the allocator instance.
-  static DiscardableMemoryShmemAllocator* GetInstance();
-
-  // Sets the allocator instance. Can only be called once, e.g. on startup.
-  // Ownership of |instance| remains with the caller.
-  static void SetInstance(DiscardableMemoryShmemAllocator* allocator);
-
-  virtual scoped_ptr<DiscardableMemoryShmemChunk>
-  AllocateLockedDiscardableMemory(size_t size) = 0;
-
- protected:
-  virtual ~DiscardableMemoryShmemAllocator() {}
-};
-
-}  // namespace base
-
-#endif  // BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index daa7782..eb0f968 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -275,31 +275,27 @@
 void MessageLoop::PostTask(
     const tracked_objects::Location& from_here,
     const Closure& task) {
-  DCHECK(!task.is_null()) << from_here.ToString();
-  incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), true);
+  message_loop_proxy_->PostTask(from_here, task);
 }
 
 void MessageLoop::PostDelayedTask(
     const tracked_objects::Location& from_here,
     const Closure& task,
     TimeDelta delay) {
-  DCHECK(!task.is_null()) << from_here.ToString();
-  incoming_task_queue_->AddToIncomingQueue(from_here, task, delay, true);
+  message_loop_proxy_->PostDelayedTask(from_here, task, delay);
 }
 
 void MessageLoop::PostNonNestableTask(
     const tracked_objects::Location& from_here,
     const Closure& task) {
-  DCHECK(!task.is_null()) << from_here.ToString();
-  incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), false);
+  message_loop_proxy_->PostNonNestableTask(from_here, task);
 }
 
 void MessageLoop::PostNonNestableDelayedTask(
     const tracked_objects::Location& from_here,
     const Closure& task,
     TimeDelta delay) {
-  DCHECK(!task.is_null()) << from_here.ToString();
-  incoming_task_queue_->AddToIncomingQueue(from_here, task, delay, false);
+  message_loop_proxy_->PostNonNestableDelayedTask(from_here, task, delay);
 }
 
 void MessageLoop::Run() {
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 7c76616..fd7596a 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -155,6 +155,9 @@
   // DestructionObserver is receiving a notification callback.
   void RemoveDestructionObserver(DestructionObserver* destruction_observer);
 
+  // NOTE: Deprecated; prefer task_runner() and the TaskRunner interfaces.
+  // TODO(skyostil): Remove these functions (crbug.com/465354).
+  //
   // The "PostTask" family of methods call the task's Run method asynchronously
   // from within a message loop at some point in the future.
   //
@@ -300,6 +303,8 @@
   }
 
   // Gets the TaskRunner associated with this message loop.
+  // TODO(skyostil): Change this to return a const reference to a refptr
+  // once the internal type matches what is being returned (crbug.com/465354).
   scoped_refptr<SingleThreadTaskRunner> task_runner() {
     return message_loop_proxy_;
   }
diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
new file mode 100644
index 0000000..57b7b35
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler.cc
@@ -0,0 +1,261 @@
+// 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/profiler/stack_sampling_profiler.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/timer/elapsed_timer.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+namespace base {
+
+namespace {
+
+// Thread-safe singleton class that stores collected profiles waiting to be
+// processed.
+class PendingProfiles {
+ public:
+  PendingProfiles();
+  ~PendingProfiles();
+
+  static PendingProfiles* GetInstance();
+
+  // Appends |profiles|. This function is thread safe.
+  void PutProfiles(const std::vector<StackSamplingProfiler::Profile>& profiles);
+  // Gets the pending profiles into *|profiles|. This function is thread safe.
+  void GetProfiles(std::vector<StackSamplingProfiler::Profile>* profiles);
+
+ private:
+  Lock profiles_lock_;
+  std::vector<StackSamplingProfiler::Profile> profiles_;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingProfiles);
+};
+
+PendingProfiles::PendingProfiles() {}
+
+PendingProfiles::~PendingProfiles() {}
+
+// static
+PendingProfiles* PendingProfiles::GetInstance() {
+  return Singleton<PendingProfiles>::get();
+}
+
+void PendingProfiles::PutProfiles(
+    const std::vector<StackSamplingProfiler::Profile>& profiles) {
+  AutoLock scoped_lock(profiles_lock_);
+  profiles_.insert(profiles_.end(), profiles.begin(), profiles.end());
+}
+
+void PendingProfiles::GetProfiles(
+    std::vector<StackSamplingProfiler::Profile>* profiles) {
+  profiles->clear();
+
+  AutoLock scoped_lock(profiles_lock_);
+  profiles_.swap(*profiles);
+}
+}  // namespace
+
+StackSamplingProfiler::Module::Module() : base_address(nullptr) {}
+
+StackSamplingProfiler::Module::~Module() {}
+
+StackSamplingProfiler::Frame::Frame()
+    : instruction_pointer(nullptr),
+      module_index(-1) {}
+
+StackSamplingProfiler::Frame::~Frame() {}
+
+StackSamplingProfiler::Profile::Profile() : preserve_sample_ordering(false) {}
+
+StackSamplingProfiler::Profile::~Profile() {}
+
+class StackSamplingProfiler::SamplingThread : public PlatformThread::Delegate {
+ public:
+  // Samples stacks using |native_sampler|. When complete, invokes
+  // |profiles_callback| with the collected profiles. |profiles_callback| must
+  // be thread-safe and may consume the contents of the vector.
+  SamplingThread(
+      scoped_ptr<NativeStackSampler> native_sampler,
+      const SamplingParams& params,
+      Callback<void(const std::vector<Profile>&)> completed_callback);
+  ~SamplingThread() override;
+
+  // Implementation of PlatformThread::Delegate:
+  void ThreadMain() override;
+
+  void Stop();
+
+ private:
+  // Collects a profile from a single burst. Returns true if the profile was
+  // collected, or false if collection was stopped before it completed.
+  bool CollectProfile(Profile* profile, TimeDelta* elapsed_time);
+  // Collects profiles from all bursts, or until the sampling is stopped. If
+  // stopped before complete, |profiles| will contains only full bursts.
+  void CollectProfiles(std::vector<Profile>* profiles);
+
+  scoped_ptr<NativeStackSampler> native_sampler_;
+
+  const SamplingParams params_;
+
+  WaitableEvent stop_event_;
+
+  Callback<void(const std::vector<Profile>&)> completed_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SamplingThread);
+};
+
+StackSamplingProfiler::SamplingThread::SamplingThread(
+    scoped_ptr<NativeStackSampler> native_sampler,
+    const SamplingParams& params,
+    Callback<void(const std::vector<Profile>&)> completed_callback)
+    : native_sampler_(native_sampler.Pass()),
+      params_(params),
+      stop_event_(false, false),
+      completed_callback_(completed_callback) {
+}
+
+StackSamplingProfiler::SamplingThread::~SamplingThread() {}
+
+void StackSamplingProfiler::SamplingThread::ThreadMain() {
+  PlatformThread::SetName("Chrome_SamplingProfilerThread");
+
+  std::vector<Profile> profiles;
+  CollectProfiles(&profiles);
+  completed_callback_.Run(profiles);
+}
+
+bool StackSamplingProfiler::SamplingThread::CollectProfile(
+    Profile* profile,
+    TimeDelta* elapsed_time) {
+  ElapsedTimer profile_timer;
+  Profile current_profile;
+  native_sampler_->ProfileRecordingStarting(&current_profile);
+  current_profile.sampling_period = params_.sampling_interval;
+  bool stopped_early = false;
+  for (int i = 0; i < params_.samples_per_burst; ++i) {
+    ElapsedTimer sample_timer;
+    current_profile.samples.push_back(Sample());
+    native_sampler_->RecordStackSample(&current_profile.samples.back());
+    TimeDelta elapsed_sample_time = sample_timer.Elapsed();
+    if (i != params_.samples_per_burst - 1) {
+      if (stop_event_.TimedWait(
+              std::max(params_.sampling_interval - elapsed_sample_time,
+                       TimeDelta()))) {
+        stopped_early = true;
+        break;
+      }
+    }
+  }
+
+  *elapsed_time = profile_timer.Elapsed();
+  current_profile.profile_duration = *elapsed_time;
+  native_sampler_->ProfileRecordingStopped();
+
+  if (!stopped_early)
+    *profile = current_profile;
+
+  return !stopped_early;
+}
+
+void StackSamplingProfiler::SamplingThread::CollectProfiles(
+    std::vector<Profile>* profiles) {
+  if (stop_event_.TimedWait(params_.initial_delay))
+    return;
+
+  for (int i = 0; i < params_.bursts; ++i) {
+    Profile profile;
+    TimeDelta elapsed_profile_time;
+    if (CollectProfile(&profile, &elapsed_profile_time))
+      profiles->push_back(profile);
+    else
+      return;
+
+    if (stop_event_.TimedWait(
+            std::max(params_.burst_interval - elapsed_profile_time,
+                     TimeDelta())))
+      return;
+  }
+}
+
+void StackSamplingProfiler::SamplingThread::Stop() {
+  stop_event_.Signal();
+}
+
+void StackSamplingProfiler::SamplingThreadDeleter::operator()(
+    SamplingThread* thread) const {
+  delete thread;
+}
+
+StackSamplingProfiler::NativeStackSampler::NativeStackSampler() {}
+
+StackSamplingProfiler::NativeStackSampler::~NativeStackSampler() {}
+
+StackSamplingProfiler::SamplingParams::SamplingParams()
+    : initial_delay(TimeDelta::FromMilliseconds(0)),
+      bursts(1),
+      burst_interval(TimeDelta::FromMilliseconds(10000)),
+      samples_per_burst(300),
+      sampling_interval(TimeDelta::FromMilliseconds(100)),
+      preserve_sample_ordering(false) {
+}
+
+StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id,
+                                             const SamplingParams& params)
+    : thread_id_(thread_id), params_(params) {}
+
+StackSamplingProfiler::~StackSamplingProfiler() {}
+
+void StackSamplingProfiler::Start() {
+  native_sampler_ = NativeStackSampler::Create(thread_id_);
+  if (!native_sampler_)
+    return;
+
+  sampling_thread_.reset(
+      new SamplingThread(
+          native_sampler_.Pass(), params_,
+          (custom_completed_callback_.is_null() ?
+           Bind(&PendingProfiles::PutProfiles,
+                Unretained(PendingProfiles::GetInstance())) :
+           custom_completed_callback_)));
+  if (!PlatformThread::CreateNonJoinable(0, sampling_thread_.get()))
+    LOG(ERROR) << "failed to create thread";
+}
+
+void StackSamplingProfiler::Stop() {
+  if (sampling_thread_)
+    sampling_thread_->Stop();
+}
+
+// static
+void StackSamplingProfiler::GetPendingProfiles(std::vector<Profile>* profiles) {
+  PendingProfiles::GetInstance()->GetProfiles(profiles);
+}
+
+void StackSamplingProfiler::SetCustomCompletedCallback(
+    Callback<void(const std::vector<Profile>&)> callback) {
+  custom_completed_callback_ = callback;
+}
+
+bool operator==(const StackSamplingProfiler::Frame &a,
+                const StackSamplingProfiler::Frame &b) {
+  return a.instruction_pointer == b.instruction_pointer &&
+      a.module_index == b.module_index;
+}
+
+bool operator<(const StackSamplingProfiler::Frame &a,
+               const StackSamplingProfiler::Frame &b) {
+  return (a.module_index < b.module_index) ||
+      (a.module_index == b.module_index &&
+       a.instruction_pointer < b.instruction_pointer);
+}
+
+}  // namespace base
diff --git a/base/profiler/stack_sampling_profiler.h b/base/profiler/stack_sampling_profiler.h
new file mode 100644
index 0000000..8d7671e
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler.h
@@ -0,0 +1,206 @@
+// 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_PROFILER_STACK_SAMPLING_PROFILER_H_
+#define BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// StackSamplingProfiler periodically stops a thread to sample its stack, for
+// the purpose of collecting information about which code paths are
+// executing. This information is used in aggregate by UMA to identify hot
+// and/or janky code paths.
+//
+// Sample StackStackSamplingProfiler usage:
+//
+//   // Create and customize params as desired.
+//   base::StackStackSamplingProfiler::SamplingParams params;
+//   // Any thread's ID may be passed as the target.
+//   base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()),
+//       params);
+//
+//   // To process the profiles within Chrome rather than via UMA, set a custom
+//   // completed callback:
+//   base::Callback<void(const std::vector<Profile>&)>
+//       thread_safe_callback = ...;
+//   profiler.SetCustomCompletedCallback(thread_safe_callback);
+//
+//   profiler.Start();
+//   // ... work being done on the target thread here ...
+//   profiler.Stop();  // optional, stops collection before complete per params
+//
+// When all profiles are complete or the profiler is stopped, if the custom
+// completed callback was set it will be called from the profiler thread with
+// the completed profiles. If no callback was set, the profiles are stored
+// internally and retrieved for UMA through
+// GetPendingProfiles(). GetPendingProfiles() should never be called by other
+// code; to retrieve profiles for in-process processing, set a completed
+// callback.
+class BASE_EXPORT StackSamplingProfiler {
+ public:
+  // Module represents the module (DLL or exe) corresponding to a stack frame.
+  struct BASE_EXPORT Module {
+    Module();
+    ~Module();
+
+    // Points to the base address of the module.
+    const void* base_address;
+    // An opaque binary string that uniquely identifies a particular program
+    // version with high probability. This is parsed from headers of the loaded
+    // module.
+    // For binaries generated by GNU tools:
+    //   Contents of the .note.gnu.build-id field.
+    // On Windows:
+    //   GUID + AGE in the debug image headers of a module.
+    std::string id;
+    // The filename of the module.
+    FilePath filename;
+  };
+
+  // Frame represents an individual sampled stack frame with module information.
+  struct BASE_EXPORT Frame {
+    Frame();
+    ~Frame();
+
+    // The sampled instruction pointer within the function.
+    const void* instruction_pointer;
+    // Index of the module in the array of modules. We don't represent module
+    // state directly here to save space.
+    int module_index;
+  };
+
+  // Sample represents a set of stack frames.
+  using Sample = std::vector<Frame>;
+
+  // Profile represents a set of samples.
+  struct BASE_EXPORT Profile {
+    Profile();
+    ~Profile();
+
+    std::vector<Module> modules;
+    std::vector<Sample> samples;
+    // Duration of this profile.
+    TimeDelta profile_duration;
+    // Time between samples.
+    TimeDelta sampling_period;
+    // True if sample ordering is important and should be preserved if and when
+    // this profile is compressed and processed.
+    bool preserve_sample_ordering;
+  };
+
+  // NativeStackSampler abstracts the native implementation required to record a
+  // stack sample for a given thread.
+  class NativeStackSampler {
+   public:
+    virtual ~NativeStackSampler();
+
+    // Create a stack sampler that records samples for |thread_handle|. Returns
+    // null if this platform does not support stack sampling.
+    static scoped_ptr<NativeStackSampler> Create(PlatformThreadId thread_id);
+
+    // Notify the sampler that we're starting to record a new profile. This
+    // function is called on the SamplingThread.
+    virtual void ProfileRecordingStarting(Profile* profile) = 0;
+
+    // Record a stack sample. This function is called on the SamplingThread.
+    virtual void RecordStackSample(Sample* sample) = 0;
+
+    // Notify the sampler that we've stopped recording the current profile. This
+    // function is called on the SamplingThread.
+    virtual void ProfileRecordingStopped() = 0;
+
+   protected:
+    NativeStackSampler();
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(NativeStackSampler);
+  };
+
+  // Represents parameters that configure the sampling.
+  struct BASE_EXPORT SamplingParams {
+    SamplingParams();
+
+    // Time to delay before first samples are taken. Defaults to 0.
+    TimeDelta initial_delay;
+    // Number of sampling bursts to perform. Defaults to 1.
+    int bursts;
+    // Interval between sampling bursts. This is the desired duration from the
+    // start of one burst to the start of the next burst. Defaults to 10s.
+    TimeDelta burst_interval;
+    // Number of samples to record per burst. Defaults to 300.
+    int samples_per_burst;
+    // Interval between samples during a sampling burst. This is the desired
+    // duration from the start of one burst to the start of the next
+    // burst. Defaults to 100ms.
+    TimeDelta sampling_interval;
+    // True if sample ordering is important and should be preserved if and when
+    // this profile is compressed and processed. Defaults to false.
+    bool preserve_sample_ordering;
+  };
+
+  StackSamplingProfiler(PlatformThreadId thread_id,
+                        const SamplingParams& params);
+  ~StackSamplingProfiler();
+
+  // Initializes the profiler and starts sampling.
+  void Start();
+  // Stops the profiler and any ongoing sampling. Calling this function is
+  // optional; if not invoked profiling will terminate when all the profiling
+  // bursts specified in the SamplingParams are completed.
+  void Stop();
+
+  // Gets the pending profiles into *|profiles| and clears the internal
+  // storage. This function is thread safe.
+  //
+  // ***This is intended for use only by UMA.*** Callers who want to process the
+  // collected profiles should use SetCustomCompletedCallback.
+  static void GetPendingProfiles(std::vector<Profile>* profiles);
+
+  // By default, collected profiles are stored internally and can be retrieved
+  // by GetPendingProfiles. If a callback is provided via this function,
+  // however, it will be called with the collected profiles instead. Note that
+  // this call to the callback occurs *on the profiler thread*.
+  void SetCustomCompletedCallback(
+      Callback<void(const std::vector<Profile>&)> callback);
+
+ private:
+  class SamplingThread;
+  struct SamplingThreadDeleter {
+    void operator() (SamplingThread* thread) const;
+  };
+
+  // The thread whose stack will be sampled.
+  PlatformThreadId thread_id_;
+
+  const SamplingParams params_;
+
+  scoped_ptr<SamplingThread, SamplingThreadDeleter> sampling_thread_;
+  scoped_ptr<NativeStackSampler> native_sampler_;
+
+  Callback<void(const std::vector<Profile>&)> custom_completed_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler);
+};
+
+// Defined to allow equality check of Samples.
+BASE_EXPORT bool operator==(const StackSamplingProfiler::Frame& a,
+                            const StackSamplingProfiler::Frame& b);
+// Defined to allow ordering of Samples.
+BASE_EXPORT bool operator<(const StackSamplingProfiler::Frame& a,
+                           const StackSamplingProfiler::Frame& b);
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_
diff --git a/base/profiler/stack_sampling_profiler_posix.cc b/base/profiler/stack_sampling_profiler_posix.cc
new file mode 100644
index 0000000..6a44d7e
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler_posix.cc
@@ -0,0 +1,14 @@
+// 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/profiler/stack_sampling_profiler.h"
+
+namespace base {
+
+scoped_ptr<StackSamplingProfiler::NativeStackSampler>
+StackSamplingProfiler::NativeStackSampler::Create(PlatformThreadId thread_id) {
+  return scoped_ptr<NativeStackSampler>();
+}
+
+}  // namespace base
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
new file mode 100644
index 0000000..ad9e926
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -0,0 +1,321 @@
+// 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 <sstream>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/path_service.h"
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+using Frame = StackSamplingProfiler::Frame;
+using Module = StackSamplingProfiler::Module;
+using Sample = StackSamplingProfiler::Sample;
+using Profile = StackSamplingProfiler::Profile;
+
+namespace {
+// A thread to target for profiling, whose stack is guaranteed to contain
+// SignalAndWaitUntilSignaled() when coordinated with the main thread.
+class TargetThread : public PlatformThread::Delegate {
+ public:
+  TargetThread();
+
+  // Implementation of PlatformThread::Delegate:
+  void ThreadMain() override;
+
+  // Wait for the thread to have started and be executing in
+  // SignalAndWaitUntilSignaled().
+  void WaitForThreadStart();
+  // Allow the thread to return from SignalAndWaitUntilSignaled() and finish
+  // execution.
+  void SignalThreadToFinish();
+
+  // This function is guaranteed to be executing between calls to
+  // WaitForThreadStart() and SignalThreadToFinish().
+  static void SignalAndWaitUntilSignaled(WaitableEvent* thread_started_event,
+                                         WaitableEvent* finish_event);
+
+  PlatformThreadId id() const { return id_; }
+
+ private:
+  WaitableEvent thread_started_event_;
+  WaitableEvent finish_event_;
+  PlatformThreadId id_;
+
+  DISALLOW_COPY_AND_ASSIGN(TargetThread);
+};
+
+TargetThread::TargetThread()
+    : thread_started_event_(false, false), finish_event_(false, false),
+      id_(0) {}
+
+void TargetThread::ThreadMain() {
+  id_ = PlatformThread::CurrentId();
+  SignalAndWaitUntilSignaled(&thread_started_event_, &finish_event_);
+}
+
+void TargetThread::WaitForThreadStart() {
+  thread_started_event_.Wait();
+}
+
+void TargetThread::SignalThreadToFinish() {
+  finish_event_.Signal();
+}
+
+// static
+// Disable inlining for this function so that it gets its own stack frame.
+NOINLINE void TargetThread::SignalAndWaitUntilSignaled(
+    WaitableEvent* thread_started_event,
+    WaitableEvent* finish_event) {
+  thread_started_event->Signal();
+  volatile int x = 1;
+  finish_event->Wait();
+  x = 0;  // Prevent tail call to WaitableEvent::Wait().
+  ALLOW_UNUSED_LOCAL(x);
+}
+
+// Called on the profiler thread when complete. Collects profiles produced by
+// the profiler, and signals an event to allow the main thread to know that that
+// the profiler is done.
+void SaveProfilesAndSignalEvent(std::vector<Profile>* profiles,
+                                WaitableEvent* event,
+                                const std::vector<Profile>& pending_profiles) {
+  *profiles = pending_profiles;
+  event->Signal();
+}
+
+// Captures profiles as specified by |params| on the TargetThread, and returns
+// them in |profiles|. Waits up to |profiler_wait_time| for the profiler to
+// complete.
+void CaptureProfiles(const StackSamplingProfiler::SamplingParams& params,
+                     std::vector<Profile>* profiles,
+                     TimeDelta profiler_wait_time) {
+  TargetThread target_thread;
+  PlatformThreadHandle target_thread_handle;
+  EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
+
+  target_thread.WaitForThreadStart();
+
+  WaitableEvent sampling_thread_completed(true, false);
+  profiles->clear();
+  StackSamplingProfiler profiler(target_thread.id(), params);
+  profiler.SetCustomCompletedCallback(
+      Bind(&SaveProfilesAndSignalEvent, Unretained(profiles),
+           Unretained(&sampling_thread_completed)));
+  profiler.Start();
+  sampling_thread_completed.TimedWait(profiler_wait_time);
+  profiler.Stop();
+  sampling_thread_completed.Wait();
+
+  target_thread.SignalThreadToFinish();
+
+  PlatformThread::Join(target_thread_handle);
+}
+
+// If this executable was linked with /INCREMENTAL (the default for non-official
+// debug and release builds on Windows), function addresses do not correspond to
+// function code itself, but instead to instructions in the Incremental Link
+// Table that jump to the functions. Check for a jump instruction and if present
+// do a little decompilation to find the function's actual starting address.
+const void* MaybeFixupFunctionAddressForILT(const void* function_address) {
+#if defined(_WIN64)
+  const unsigned char* opcode =
+      reinterpret_cast<const unsigned char*>(function_address);
+  if (*opcode == 0xe9) {
+    // This is a relative jump instruction. Assume we're in the ILT and compute
+    // the function start address from the instruction offset.
+    const unsigned char* offset = opcode + 1;
+    const unsigned char* next_instruction = opcode + 5;
+    return next_instruction +
+        static_cast<int64>(*reinterpret_cast<const int32*>(offset));
+  }
+#endif
+  return function_address;
+}
+
+// Searches through the frames in |sample|, returning an iterator to the first
+// frame that has an instruction pointer between |function_address| and
+// |function_address| + |size|. Returns sample.end() if no such frames are
+// found.
+Sample::const_iterator FindFirstFrameWithinFunction(
+    const Sample& sample,
+    const void* function_address,
+    int function_size) {
+  function_address = MaybeFixupFunctionAddressForILT(function_address);
+  for (auto it = sample.begin(); it != sample.end(); ++it) {
+    if ((reinterpret_cast<const unsigned char*>(it->instruction_pointer) >=
+         reinterpret_cast<const unsigned char*>(function_address)) &&
+        (reinterpret_cast<const unsigned char*>(it->instruction_pointer) <
+         (reinterpret_cast<const unsigned char*>(function_address) +
+          function_size)))
+      return it;
+  }
+  return sample.end();
+}
+
+// Formats a sample into a string that can be output for test diagnostics.
+std::string FormatSampleForDiagnosticOutput(
+    const Sample& sample,
+    const std::vector<Module>& modules) {
+  std::ostringstream stream;
+  for (const Frame& frame: sample) {
+    stream << frame.instruction_pointer << " "
+           << modules[frame.module_index].filename.value() << std::endl;
+  }
+  return stream.str();
+}
+
+// Returns a duration that is longer than the test timeout. We would use
+// TimeDelta::Max() but https://crbug.com/465948.
+TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); }
+}  // namespace
+
+
+// The tests below are enabled for Win x64 only, pending implementation of the
+// tested functionality on other platforms/architectures.
+
+// Checks that the basic expected information is present in a sampled profile.
+#if defined(_WIN64)
+#define MAYBE_Basic Basic
+#else
+#define MAYBE_Basic DISABLED_Basic
+#endif
+TEST(StackSamplingProfilerTest, MAYBE_Basic) {
+  StackSamplingProfiler::SamplingParams params;
+  params.initial_delay = params.burst_interval = params.sampling_interval =
+      TimeDelta::FromMilliseconds(0);
+  params.bursts = 1;
+  params.samples_per_burst = 1;
+
+  std::vector<Profile> profiles;
+  CaptureProfiles(params, &profiles, AVeryLongTimeDelta());
+
+  // Check that the profile and samples sizes are correct, and the module
+  // indices are in range.
+
+  ASSERT_EQ(1u, profiles.size());
+  const Profile& profile = profiles[0];
+  ASSERT_EQ(1u, profile.samples.size());
+  EXPECT_EQ(params.sampling_interval, profile.sampling_period);
+  const Sample& sample = profile.samples[0];
+  for (const auto& frame : sample) {
+    ASSERT_GE(frame.module_index, 0);
+    ASSERT_LT(frame.module_index, static_cast<int>(profile.modules.size()));
+  }
+
+  // Check that the stack contains a frame for
+  // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this
+  // executable's module.
+
+  // Since we don't have a good way to know the function size, use 100 bytes as
+  // a reasonable window to locate the instruction pointer.
+  Sample::const_iterator loc = FindFirstFrameWithinFunction(
+      sample,
+      reinterpret_cast<const void*>(&TargetThread::SignalAndWaitUntilSignaled),
+      100);
+  ASSERT_TRUE(loc != sample.end())
+      << "Function at "
+      << MaybeFixupFunctionAddressForILT(
+          reinterpret_cast<const void*>(
+              &TargetThread::SignalAndWaitUntilSignaled))
+      << " was not found in stack:" << std::endl
+      << FormatSampleForDiagnosticOutput(sample, profile.modules);
+
+  FilePath executable_path;
+  bool got_executable_path = PathService::Get(FILE_EXE, &executable_path);
+  EXPECT_TRUE(got_executable_path);
+  EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename);
+}
+
+// Checks that the expected number of profiles and samples are present in the
+// profiles produced.
+#if defined(_WIN64)
+#define MAYBE_MultipleProfilesAndSamples MultipleProfilesAndSamples
+#else
+#define MAYBE_MultipleProfilesAndSamples DISABLED_MultipleProfilesAndSamples
+#endif
+TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) {
+  StackSamplingProfiler::SamplingParams params;
+  params.initial_delay = params.burst_interval = params.sampling_interval =
+      TimeDelta::FromMilliseconds(0);
+  params.bursts = 2;
+  params.samples_per_burst = 3;
+
+  std::vector<Profile> profiles;
+  CaptureProfiles(params, &profiles, AVeryLongTimeDelta());
+
+  ASSERT_EQ(2u, profiles.size());
+  EXPECT_EQ(3u, profiles[0].samples.size());
+  EXPECT_EQ(3u, profiles[1].samples.size());
+}
+
+// Checks that no profiles are captured if the profiling is stopped during the
+// initial delay.
+#if defined(_WIN64)
+#define MAYBE_StopDuringInitialDelay StopDuringInitialDelay
+#else
+#define MAYBE_StopDuringInitialDelay DISABLED_StopDuringInitialDelay
+#endif
+TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) {
+  StackSamplingProfiler::SamplingParams params;
+  params.burst_interval = params.sampling_interval =
+      TimeDelta::FromMilliseconds(0);
+  params.initial_delay = TimeDelta::FromSeconds(60);
+  params.bursts = params.samples_per_burst = 1;
+
+  std::vector<Profile> profiles;
+  CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(0));
+
+  EXPECT_TRUE(profiles.empty());
+}
+
+// Checks that the single completed profile is captured if the profiling is
+// stopped between bursts.
+#if defined(_WIN64)
+#define MAYBE_StopDuringInterBurstInterval StopDuringInterBurstInterval
+#else
+#define MAYBE_StopDuringInterBurstInterval DISABLED_StopDuringInterBurstInterval
+#endif
+TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) {
+  StackSamplingProfiler::SamplingParams params;
+  params.initial_delay = params.sampling_interval =
+      TimeDelta::FromMilliseconds(0);
+  params.burst_interval = TimeDelta::FromSeconds(60);
+  params.bursts = 2;
+  params.samples_per_burst = 1;
+
+  std::vector<Profile> profiles;
+  CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50));
+
+  ASSERT_EQ(1u, profiles.size());
+  EXPECT_EQ(1u, profiles[0].samples.size());
+}
+
+// Checks that only completed profiles are captured.
+#if defined(_WIN64)
+#define MAYBE_StopDuringInterSampleInterval StopDuringInterSampleInterval
+#else
+#define MAYBE_StopDuringInterSampleInterval \
+  DISABLED_StopDuringInterSampleInterval
+#endif
+TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterSampleInterval) {
+  StackSamplingProfiler::SamplingParams params;
+  params.initial_delay = params.burst_interval = TimeDelta::FromMilliseconds(0);
+  params.sampling_interval = TimeDelta::FromSeconds(60);
+  params.bursts = 1;
+  params.samples_per_burst = 2;
+
+  std::vector<Profile> profiles;
+  CaptureProfiles(params, &profiles, TimeDelta::FromMilliseconds(50));
+
+  EXPECT_TRUE(profiles.empty());
+}
+
+}  // namespace tracked_objects
diff --git a/base/profiler/stack_sampling_profiler_win.cc b/base/profiler/stack_sampling_profiler_win.cc
new file mode 100644
index 0000000..ba46cf0
--- /dev/null
+++ b/base/profiler/stack_sampling_profiler_win.cc
@@ -0,0 +1,325 @@
+// 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/profiler/stack_sampling_profiler.h"
+
+#include <dbghelp.h>
+#include <map>
+#include <utility>
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "base/win/pe_image.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+
+namespace {
+
+class NativeStackSamplerWin : public StackSamplingProfiler::NativeStackSampler {
+ public:
+  explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
+  ~NativeStackSamplerWin() override;
+
+  // StackSamplingProfiler::NativeStackSampler:
+  void ProfileRecordingStarting(
+      StackSamplingProfiler::Profile* profile) override;
+  void RecordStackSample(StackSamplingProfiler::Sample* sample) override;
+  void ProfileRecordingStopped() override;
+
+ private:
+  static bool GetModuleInfo(HMODULE module,
+                            StackSamplingProfiler::Module* module_info);
+
+  void CopyToSample(const void* const instruction_pointers[],
+                    const HMODULE modules[],
+                    int stack_depth,
+                    StackSamplingProfiler::Sample* sample,
+                    std::vector<StackSamplingProfiler::Module>* module_infos);
+
+  win::ScopedHandle thread_handle_;
+  // Weak. Points to the profile being recorded between
+  // ProfileRecordingStarting() and ProfileRecordingStopped().
+  StackSamplingProfiler::Profile* current_profile_;
+  // Maps a module to the module's index within current_profile_->modules.
+  std::map<HMODULE, int> profile_module_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin);
+};
+
+// Walk the stack represented by |context| from the current frame downwards,
+// recording the instruction pointers for each frame in |instruction_pointers|.
+int RecordStack(CONTEXT* context,
+                int max_stack_size,
+                const void* instruction_pointers[],
+                bool* last_frame_is_unknown_function) {
+#ifdef _WIN64
+  *last_frame_is_unknown_function = false;
+
+  IMAGEHLP_SYMBOL64 sym;
+  sym.SizeOfStruct = sizeof(sym);
+  sym.MaxNameLength = 0;
+
+  for (int i = 0; i < max_stack_size; ++i) {
+    // Try to look up unwind metadata for the current function.
+    ULONG64 image_base;
+    PRUNTIME_FUNCTION runtime_function =
+        RtlLookupFunctionEntry(context->Rip, &image_base, nullptr);
+
+    instruction_pointers[i] = reinterpret_cast<void*>(context->Rip);
+
+    if (runtime_function) {
+      KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0};
+      void* handler_data;
+      ULONG64 establisher_frame;
+      RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
+          &handler_data, &establisher_frame, &nvcontext);
+    } else {
+      // If we don't have a RUNTIME_FUNCTION, then we've encountered
+      // a leaf function.  Adjust the stack appropriately.
+      context->Rip = *reinterpret_cast<PDWORD64>(context->Rsp);
+      context->Rsp += 8;
+      *last_frame_is_unknown_function = true;
+    }
+
+    if (!context->Rip)
+      return i;
+  }
+  return max_stack_size;
+#else
+  return 0;
+#endif
+}
+
+// Fills in |modules| corresponding to the pointers to code in |addresses|. The
+// modules are returned with reference counts incremented should be freed with
+// FreeModules.
+void FindModulesForAddresses(const void* const addresses[], HMODULE modules[],
+                             int stack_depth,
+                             bool last_frame_is_unknown_function) {
+  const int module_frames = last_frame_is_unknown_function ? stack_depth - 1 :
+      stack_depth;
+  for (int i = 0; i < module_frames; ++i) {
+    HMODULE module = NULL;
+    if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+                          reinterpret_cast<LPCTSTR>(addresses[i]),
+                          &module)) {
+      // HMODULE is the base address of the module.
+      DCHECK_LT(reinterpret_cast<const void*>(module), addresses[i]);
+      modules[i] = module;
+    }
+  }
+}
+
+// Free the modules returned by FindModulesForAddresses.
+void FreeModules(int stack_depth, HMODULE modules[]) {
+  for (int i = 0; i < stack_depth; ++i) {
+    if (modules[i])
+      ::FreeLibrary(modules[i]);
+  }
+}
+
+// Disables priority boost on a thread for the lifetime of the object.
+class ScopedDisablePriorityBoost {
+ public:
+  ScopedDisablePriorityBoost(HANDLE thread_handle);
+  ~ScopedDisablePriorityBoost();
+
+ private:
+  HANDLE thread_handle_;
+  BOOL got_previous_boost_state_;
+  BOOL boost_state_was_disabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost);
+};
+
+ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle)
+    : thread_handle_(thread_handle),
+      got_previous_boost_state_(false),
+      boost_state_was_disabled_(false) {
+  got_previous_boost_state_ =
+      ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_);
+  if (got_previous_boost_state_ && !boost_state_was_disabled_) {
+    // Confusingly, TRUE disables priority boost ...
+    ::SetThreadPriorityBoost(thread_handle_, TRUE);
+  }
+}
+
+ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
+  if (got_previous_boost_state_ && !boost_state_was_disabled_) {
+    // ... and FALSE enables priority boost.
+    ::SetThreadPriorityBoost(thread_handle_, FALSE);
+  }
+}
+
+// Suspends the thread with |thread_handle|, records the stack into
+// |instruction_pointers|, then resumes the thread. Returns the size of the
+// stack.
+int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size,
+                                const void* instruction_pointers[],
+                                bool* last_frame_is_unknown_function) {
+#if defined(_WIN64)
+  if (RtlVirtualUnwind == nullptr || RtlLookupFunctionEntry == nullptr)
+    return 0;
+#endif
+
+  if (::SuspendThread(thread_handle) == -1) {
+    LOG(ERROR) << "SuspendThread failed: " << GetLastError();
+    return 0;
+  }
+
+  CONTEXT thread_context = {0};
+  thread_context.ContextFlags = CONTEXT_FULL;
+  if (!::GetThreadContext(thread_handle, &thread_context)) {
+    LOG(ERROR) << "GetThreadContext failed: " << GetLastError();
+  }
+
+  int stack_depth = RecordStack(&thread_context, max_stack_size,
+                                instruction_pointers,
+                                last_frame_is_unknown_function);
+
+  {
+    ScopedDisablePriorityBoost disable_priority_boost(thread_handle);
+    if (::ResumeThread(thread_handle) == -1)
+      LOG(ERROR) << "ResumeThread failed: " << GetLastError();
+  }
+
+  return stack_depth;
+}
+
+}  // namespace
+
+scoped_ptr<StackSamplingProfiler::NativeStackSampler>
+StackSamplingProfiler::NativeStackSampler::Create(PlatformThreadId thread_id) {
+#if _WIN64
+  // Get the thread's handle.
+  HANDLE thread_handle = ::OpenThread(
+      THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
+      FALSE,
+      thread_id);
+  DCHECK(thread_handle) << "OpenThread failed";
+
+  return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin(
+      win::ScopedHandle(thread_handle)));
+#else
+  return scoped_ptr<NativeStackSampler>();
+#endif
+}
+
+NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle)
+    : thread_handle_(thread_handle.Take()) {
+#ifdef _WIN64
+  if (RtlVirtualUnwind == nullptr && RtlLookupFunctionEntry == nullptr) {
+    const HMODULE nt_dll_handle = ::GetModuleHandle(L"ntdll.dll");
+    // This should always be non-null, but handle just in case.
+    if (nt_dll_handle) {
+      reinterpret_cast<void*&>(RtlVirtualUnwind) =
+          ::GetProcAddress(nt_dll_handle, "RtlVirtualUnwind");
+      reinterpret_cast<void*&>(RtlLookupFunctionEntry) =
+          ::GetProcAddress(nt_dll_handle, "RtlLookupFunctionEntry");
+    }
+  }
+#endif
+}
+
+NativeStackSamplerWin::~NativeStackSamplerWin() {
+}
+
+void NativeStackSamplerWin::ProfileRecordingStarting(
+    StackSamplingProfiler::Profile* profile) {
+  current_profile_ = profile;
+  profile_module_index_.clear();
+}
+
+void NativeStackSamplerWin::RecordStackSample(
+    StackSamplingProfiler::Sample* sample) {
+  DCHECK(current_profile_);
+
+  const int max_stack_size = 64;
+  const void* instruction_pointers[max_stack_size] = {0};
+  HMODULE modules[max_stack_size] = {0};
+
+  bool last_frame_is_unknown_function = false;
+  int stack_depth = SuspendThreadAndRecordStack(
+      thread_handle_.Get(), max_stack_size, instruction_pointers,
+      &last_frame_is_unknown_function);
+  FindModulesForAddresses(instruction_pointers, modules, stack_depth,
+                          last_frame_is_unknown_function);
+  CopyToSample(instruction_pointers, modules, stack_depth, sample,
+               &current_profile_->modules);
+  FreeModules(stack_depth, modules);
+}
+
+void NativeStackSamplerWin::ProfileRecordingStopped() {
+  current_profile_ = nullptr;
+}
+
+// static
+bool NativeStackSamplerWin::GetModuleInfo(
+    HMODULE module,
+    StackSamplingProfiler::Module* module_info) {
+  wchar_t module_name[MAX_PATH];
+  DWORD result_length =
+      GetModuleFileName(module, module_name, arraysize(module_name));
+  if (result_length == 0)
+    return false;
+
+  module_info->filename = base::FilePath(module_name);
+
+  module_info->base_address = reinterpret_cast<const void*>(module);
+
+  GUID guid;
+  DWORD age;
+  win::PEImage(module).GetDebugId(&guid, &age);
+  module_info->id.insert(module_info->id.end(),
+                         reinterpret_cast<char*>(&guid),
+                         reinterpret_cast<char*>(&guid + 1));
+  module_info->id.insert(module_info->id.end(),
+                         reinterpret_cast<char*>(&age),
+                         reinterpret_cast<char*>(&age + 1));
+
+  return true;
+}
+
+void NativeStackSamplerWin::CopyToSample(
+    const void* const instruction_pointers[],
+    const HMODULE modules[],
+    int stack_depth,
+    StackSamplingProfiler::Sample* sample,
+    std::vector<StackSamplingProfiler::Module>* module_infos) {
+  sample->clear();
+  sample->reserve(stack_depth);
+
+  for (int i = 0; i < stack_depth; ++i) {
+    sample->push_back(StackSamplingProfiler::Frame());
+    StackSamplingProfiler::Frame& frame = sample->back();
+
+    frame.instruction_pointer = instruction_pointers[i];
+
+    // Record an invalid module index if we don't have a valid module.
+    if (!modules[i]) {
+      frame.module_index = -1;
+      continue;
+    }
+
+    auto loc = profile_module_index_.find(modules[i]);
+    if (loc == profile_module_index_.end()) {
+      StackSamplingProfiler::Module module_info;
+      // Record an invalid module index if we have a module but can't find
+      // information on it.
+      if (!GetModuleInfo(modules[i], &module_info)) {
+        frame.module_index = -1;
+        continue;
+      }
+      module_infos->push_back(module_info);
+      loc = profile_module_index_.insert(std::make_pair(
+          modules[i], static_cast<int>(module_infos->size() - 1))).first;
+    }
+
+    frame.module_index = loc->second;
+  }
+}
+
+}  // namespace base
diff --git a/base/stl_util.h b/base/stl_util.h
index 3602df0..89a53b0 100644
--- a/base/stl_util.h
+++ b/base/stl_util.h
@@ -148,8 +148,7 @@
 void STLDeleteValues(T* container) {
   if (!container)
     return;
-  for (typename T::iterator i(container->begin()); i != container->end(); ++i)
-    delete i->second;
+  STLDeleteContainerPairSecondPointers(container->begin(), container->end());
   container->clear();
 }
 
diff --git a/base/strings/string16.h b/base/strings/string16.h
index 804dca4..1a01a96 100644
--- a/base/strings/string16.h
+++ b/base/strings/string16.h
@@ -94,7 +94,7 @@
     return c16memchr(s, a, n);
   }
 
-  static char_type* move(char_type* s1, const char_type* s2, int_type n) {
+  static char_type* move(char_type* s1, const char_type* s2, size_t n) {
     return c16memmove(s1, s2, n);
   }
 
diff --git a/base/sys_info.h b/base/sys_info.h
index 660343d..d3476d9 100644
--- a/base/sys_info.h
+++ b/base/sys_info.h
@@ -48,6 +48,10 @@
   // or -1 on failure.
   static int64 AmountOfFreeDiskSpace(const FilePath& path);
 
+  // Determine whether the device that services |path| has a seek penalty.
+  // Returns false if it couldn't be determined (e.g., |path| doesn't exist).
+  static bool HasSeekPenalty(const FilePath& path, bool* has_seek_penalty);
+
   // Returns system uptime in milliseconds.
   static int64 Uptime();
 
diff --git a/base/sys_info_android.cc b/base/sys_info_android.cc
index 245097f..0f2abc5 100644
--- a/base/sys_info_android.cc
+++ b/base/sys_info_android.cc
@@ -155,6 +155,12 @@
 
 namespace base {
 
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  // Find a case where this is incorrect and dbeam@ will buy you a beer.
+  *has_seek_penalty = false;
+  return true;
+}
+
 std::string SysInfo::OperatingSystemName() {
   return "Android";
 }
diff --git a/base/sys_info_chromeos.cc b/base/sys_info_chromeos.cc
index ef5f1fe..2a5f5d7 100644
--- a/base/sys_info_chromeos.cc
+++ b/base/sys_info_chromeos.cc
@@ -169,6 +169,11 @@
 
 }  // namespace
 
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  // TODO(dbeam): implement. Can we just use boards/models?
+  return false;
+}
+
 // static
 void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
                                             int32* minor_version,
diff --git a/base/sys_info_freebsd.cc b/base/sys_info_freebsd.cc
index 832b359..338f1cd 100644
--- a/base/sys_info_freebsd.cc
+++ b/base/sys_info_freebsd.cc
@@ -22,6 +22,10 @@
   return static_cast<int64>(pages) * page_size;
 }
 
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  return false;
+}
+
 // static
 size_t SysInfo::MaxSharedMemorySize() {
   size_t limit;
diff --git a/base/sys_info_ios.mm b/base/sys_info_ios.mm
index 49d618c..324bef6 100644
--- a/base/sys_info_ios.mm
+++ b/base/sys_info_ios.mm
@@ -16,6 +16,12 @@
 
 namespace base {
 
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  // Find a case where this is incorrect and dbeam@ will buy you a beer.
+  *has_seek_penalty = false;
+  return true;
+}
+
 // static
 std::string SysInfo::OperatingSystemName() {
   static dispatch_once_t get_system_name_once;
diff --git a/base/sys_info_linux.cc b/base/sys_info_linux.cc
index c698f91..401d55c 100644
--- a/base/sys_info_linux.cc
+++ b/base/sys_info_linux.cc
@@ -68,6 +68,13 @@
   return g_lazy_physical_memory.Get().value();
 }
 
+#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  // TODO(dbeam): implement.
+  return false;
+}
+#endif
+
 // static
 size_t SysInfo::MaxSharedMemorySize() {
   return g_lazy_max_shared_memory.Get().value();
diff --git a/base/sys_info_mac.cc b/base/sys_info_mac.cc
index 18df624..8635088 100644
--- a/base/sys_info_mac.cc
+++ b/base/sys_info_mac.cc
@@ -76,6 +76,11 @@
       vm_info.free_count - vm_info.speculative_count) * PAGE_SIZE;
 }
 
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  // TODO(dbeam): implement.
+  return false;
+}
+
 // static
 std::string SysInfo::CPUModelName() {
   char name[256];
diff --git a/base/sys_info_openbsd.cc b/base/sys_info_openbsd.cc
index edbb2c9..68d2ea1 100644
--- a/base/sys_info_openbsd.cc
+++ b/base/sys_info_openbsd.cc
@@ -48,6 +48,10 @@
   return AmountOfMemory(_SC_AVPHYS_PAGES);
 }
 
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  return false;
+}
+
 // static
 size_t SysInfo::MaxSharedMemorySize() {
   int mib[] = { CTL_KERN, KERN_SHMINFO, KERN_SHMINFO_SHMMAX };
diff --git a/base/sys_info_unittest.cc b/base/sys_info_unittest.cc
index 15ae098..da47861 100644
--- a/base/sys_info_unittest.cc
+++ b/base/sys_info_unittest.cc
@@ -41,6 +41,13 @@
             << tmp_path.value();
 }
 
+TEST_F(SysInfoTest, HasSeekPenalty) {
+  FilePath tmp_path;
+  ASSERT_TRUE(base::GetTempDir(&tmp_path));
+  bool unused;
+  base::SysInfo::HasSeekPenalty(tmp_path, &unused);
+}
+
 #if defined(OS_WIN) || defined(OS_MACOSX)
 TEST_F(SysInfoTest, OperatingSystemVersionNumbers) {
   int32 os_major_version = -1;
diff --git a/base/sys_info_win.cc b/base/sys_info_win.cc
index 9cc0cfa..817992c 100644
--- a/base/sys_info_win.cc
+++ b/base/sys_info_win.cc
@@ -5,7 +5,9 @@
 #include "base/sys_info.h"
 
 #include <windows.h>
+#include <winioctl.h>
 
+#include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
@@ -24,9 +26,7 @@
   }
 
   int64 rv = static_cast<int64>(memory_info.*memory_field);
-  if (rv < 0)
-    rv = kint64max;
-  return rv;
+  return rv < 0 ? kint64max : rv;
 }
 
 }  // namespace
@@ -55,16 +55,51 @@
 
 // static
 int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
-  base::ThreadRestrictions::AssertIOAllowed();
+  ThreadRestrictions::AssertIOAllowed();
 
   ULARGE_INTEGER available, total, free;
-  if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free)) {
+  if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free))
     return -1;
-  }
+
   int64 rv = static_cast<int64>(available.QuadPart);
-  if (rv < 0)
-    rv = kint64max;
-  return rv;
+  return rv < 0 ? kint64max : rv;
+}
+
+bool SysInfo::HasSeekPenalty(const FilePath& path, bool* has_seek_penalty) {
+  ThreadRestrictions::AssertIOAllowed();
+
+  DCHECK(path.IsAbsolute());
+  DCHECK(has_seek_penalty);
+
+  // TODO(dbeam): Vista, XP support.
+  if (win::GetVersion() < win::VERSION_WIN7)
+    return false;
+
+  std::vector<FilePath::StringType> components;
+  path.GetComponents(&components);
+
+  File drive(FilePath(L"\\\\.\\" + components[0]), File::FLAG_OPEN);
+  if (!drive.IsValid())
+    return false;
+
+  STORAGE_PROPERTY_QUERY query = {};
+  query.QueryType = PropertyStandardQuery;
+  query.PropertyId = StorageDeviceSeekPenaltyProperty;
+
+  DEVICE_SEEK_PENALTY_DESCRIPTOR result;
+  DWORD bytes_returned;
+
+  BOOL success = DeviceIoControl(drive.GetPlatformFile(),
+                                 IOCTL_STORAGE_QUERY_PROPERTY,
+                                 &query, sizeof(query),
+                                 &result, sizeof(result),
+                                 &bytes_returned,
+                                 NULL);
+  if (success == FALSE || bytes_returned < sizeof(result))
+    return false;
+
+  *has_seek_penalty = result.IncursSeekPenalty != FALSE;
+  return true;
 }
 
 // static
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index f5d2e4c..c4356cb 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -81,8 +81,8 @@
     "simple_test_tick_clock.h",
     "task_runner_test_template.cc",
     "task_runner_test_template.h",
-    "test_discardable_memory_shmem_allocator.cc",
-    "test_discardable_memory_shmem_allocator.h",
+    "test_discardable_memory_allocator.cc",
+    "test_discardable_memory_allocator.h",
     "test_file_util.cc",
     "test_file_util.h",
     "test_file_util_android.cc",
diff --git a/base/test/test_discardable_memory_allocator.cc b/base/test/test_discardable_memory_allocator.cc
new file mode 100644
index 0000000..cb2cccf
--- /dev/null
+++ b/base/test/test_discardable_memory_allocator.cc
@@ -0,0 +1,40 @@
+// 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/test/test_discardable_memory_allocator.h"
+
+#include <stdint.h>
+
+#include "base/memory/discardable_memory.h"
+
+namespace base {
+namespace {
+
+class DiscardableMemoryImpl : public DiscardableMemory {
+ public:
+  explicit DiscardableMemoryImpl(size_t size) : memory_(new uint8_t[size]) {}
+
+  // Overridden from DiscardableMemory:
+  bool Lock() override { return false; }
+  void Unlock() override {}
+  void* Memory() const override { return memory_.get(); }
+
+ private:
+  scoped_ptr<uint8_t[]> memory_;
+};
+
+}  // namespace
+
+TestDiscardableMemoryAllocator::TestDiscardableMemoryAllocator() {
+}
+
+TestDiscardableMemoryAllocator::~TestDiscardableMemoryAllocator() {
+}
+
+scoped_ptr<DiscardableMemory>
+TestDiscardableMemoryAllocator::AllocateLockedDiscardableMemory(size_t size) {
+  return make_scoped_ptr(new DiscardableMemoryImpl(size));
+}
+
+}  // namespace base
diff --git a/base/test/test_discardable_memory_allocator.h b/base/test/test_discardable_memory_allocator.h
new file mode 100644
index 0000000..df9d469
--- /dev/null
+++ b/base/test/test_discardable_memory_allocator.h
@@ -0,0 +1,30 @@
+// 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_TEST_TEST_DISCARDABLE_MEMORY_ALLOCATOR_H_
+#define BASE_TEST_TEST_DISCARDABLE_MEMORY_ALLOCATOR_H_
+
+#include "base/memory/discardable_memory_allocator.h"
+
+namespace base {
+
+// TestDiscardableMemoryAllocator is a simple DiscardableMemoryAllocator
+// implementation that can be used for testing. It allocates one-shot
+// DiscardableMemory instances backed by heap memory.
+class TestDiscardableMemoryAllocator : public DiscardableMemoryAllocator {
+ public:
+  TestDiscardableMemoryAllocator();
+  ~TestDiscardableMemoryAllocator() override;
+
+  // Overridden from DiscardableMemoryAllocator:
+  scoped_ptr<DiscardableMemory> AllocateLockedDiscardableMemory(
+      size_t size) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemoryAllocator);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/base/test/test_discardable_memory_shmem_allocator.cc b/base/test/test_discardable_memory_shmem_allocator.cc
deleted file mode 100644
index 24185a2..0000000
--- a/base/test/test_discardable_memory_shmem_allocator.cc
+++ /dev/null
@@ -1,40 +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.
-
-#include "base/test/test_discardable_memory_shmem_allocator.h"
-
-#include <stdint.h>
-
-namespace base {
-namespace {
-
-class DiscardableMemoryShmemChunkImpl : public DiscardableMemoryShmemChunk {
- public:
-  explicit DiscardableMemoryShmemChunkImpl(size_t size)
-      : memory_(new uint8_t[size]) {}
-
-  // Overridden from DiscardableMemoryShmemChunk:
-  bool Lock() override { return false; }
-  void Unlock() override {}
-  void* Memory() const override { return memory_.get(); }
-
- private:
-  scoped_ptr<uint8_t[]> memory_;
-};
-
-}  // namespace
-
-TestDiscardableMemoryShmemAllocator::TestDiscardableMemoryShmemAllocator() {
-}
-
-TestDiscardableMemoryShmemAllocator::~TestDiscardableMemoryShmemAllocator() {
-}
-
-scoped_ptr<DiscardableMemoryShmemChunk>
-TestDiscardableMemoryShmemAllocator::AllocateLockedDiscardableMemory(
-    size_t size) {
-  return make_scoped_ptr(new DiscardableMemoryShmemChunkImpl(size));
-}
-
-}  // namespace base
diff --git a/base/test/test_discardable_memory_shmem_allocator.h b/base/test/test_discardable_memory_shmem_allocator.h
deleted file mode 100644
index a40960e..0000000
--- a/base/test/test_discardable_memory_shmem_allocator.h
+++ /dev/null
@@ -1,28 +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_TEST_TEST_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_
-#define BASE_TEST_TEST_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_
-
-#include "base/memory/discardable_memory_shmem_allocator.h"
-
-namespace base {
-
-class TestDiscardableMemoryShmemAllocator
-    : public DiscardableMemoryShmemAllocator {
- public:
-  TestDiscardableMemoryShmemAllocator();
-  ~TestDiscardableMemoryShmemAllocator() override;
-
-  // Overridden from DiscardableMemoryShmemAllocator:
-  scoped_ptr<DiscardableMemoryShmemChunk> AllocateLockedDiscardableMemory(
-      size_t size) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemoryShmemAllocator);
-};
-
-}  // namespace base
-
-#endif  // BASE_TEST_TEST_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_
diff --git a/base/test/test_mock_time_task_runner.cc b/base/test/test_mock_time_task_runner.cc
index 6bcab8b..8cc2e6d 100644
--- a/base/test/test_mock_time_task_runner.cc
+++ b/base/test/test_mock_time_task_runner.cc
@@ -164,6 +164,10 @@
   return PostDelayedTask(from_here, task, delay);
 }
 
+bool TestMockTimeTaskRunner::IsElapsingStopped() {
+  return false;
+}
+
 void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
   // Empty default implementation.
 }
@@ -179,7 +183,7 @@
 void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
   DCHECK_GE(max_delta, TimeDelta());
   const TimeTicks original_now_ticks = now_ticks_;
-  while (true) {
+  while (!IsElapsingStopped()) {
     OnBeforeSelectingTask();
     TestPendingTask task_info;
     if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
diff --git a/base/test/test_mock_time_task_runner.h b/base/test/test_mock_time_task_runner.h
index c38fd6d..705af10 100644
--- a/base/test/test_mock_time_task_runner.h
+++ b/base/test/test_mock_time_task_runner.h
@@ -94,6 +94,11 @@
  protected:
   ~TestMockTimeTaskRunner() override;
 
+  // Whether the elapsing of virtual time is stopped or not. Subclasses can
+  // override this method to perform early exits from a running task runner.
+  // Defaults to always return false.
+  virtual bool IsElapsingStopped();
+
   // Called before the next task to run is selected, so that subclasses have a
   // last chance to make sure all tasks are posted.
   virtual void OnBeforeSelectingTask();
diff --git a/base/threading/platform_thread_android.cc b/base/threading/platform_thread_android.cc
index f8395e5..aab4c19 100644
--- a/base/threading/platform_thread_android.cc
+++ b/base/threading/platform_thread_android.cc
@@ -7,80 +7,57 @@
 #include <errno.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "base/android/jni_android.h"
 #include "base/android/thread_utils.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/thread_id_name_manager.h"
 #include "base/tracked_objects.h"
 #include "jni/ThreadUtils_jni.h"
 
 namespace base {
 
-namespace {
-int ThreadNiceValue(ThreadPriority priority) {
-  // These nice values are taken from Android, which uses nice
-  // values like linux, but defines some preset nice values.
-  //   Process.THREAD_PRIORITY_AUDIO = -16
-  //   Process.THREAD_PRIORITY_BACKGROUND = 10
-  //   Process.THREAD_PRIORITY_DEFAULT = 0;
-  //   Process.THREAD_PRIORITY_DISPLAY = -4;
-  //   Process.THREAD_PRIORITY_FOREGROUND = -2;
-  //   Process.THREAD_PRIORITY_LESS_FAVORABLE = 1;
-  //   Process.THREAD_PRIORITY_LOWEST = 19;
-  //   Process.THREAD_PRIORITY_MORE_FAVORABLE = -1;
-  //   Process.THREAD_PRIORITY_URGENT_AUDIO = -19;
-  //   Process.THREAD_PRIORITY_URGENT_DISPLAY = -8;
-  // We use -6 for display, but we may want to split this
-  // into urgent (-8) and non-urgent (-4).
-  static const int threadPriorityAudio = -16;
-  static const int threadPriorityBackground = 10;
-  static const int threadPriorityDefault = 0;
-  static const int threadPriorityDisplay = -6;
-  switch (priority) {
-    case kThreadPriority_RealtimeAudio:
-      return threadPriorityAudio;
-    case kThreadPriority_Background:
-      return threadPriorityBackground;
-    case kThreadPriority_Normal:
-      return threadPriorityDefault;
-    case kThreadPriority_Display:
-      return threadPriorityDisplay;
-    default:
-      NOTREACHED() << "Unknown priority.";
-      return 0;
-  }
-}
-} // namespace
+namespace internal {
 
-//static
-void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
-                                       ThreadPriority priority) {
+// These nice values are taken from Android, which uses nice values like linux,
+// but defines some preset nice values.
+//   Process.THREAD_PRIORITY_AUDIO = -16
+//   Process.THREAD_PRIORITY_BACKGROUND = 10
+//   Process.THREAD_PRIORITY_DEFAULT = 0;
+//   Process.THREAD_PRIORITY_DISPLAY = -4;
+//   Process.THREAD_PRIORITY_FOREGROUND = -2;
+//   Process.THREAD_PRIORITY_LESS_FAVORABLE = 1;
+//   Process.THREAD_PRIORITY_LOWEST = 19;
+//   Process.THREAD_PRIORITY_MORE_FAVORABLE = -1;
+//   Process.THREAD_PRIORITY_URGENT_AUDIO = -19;
+//   Process.THREAD_PRIORITY_URGENT_DISPLAY = -8;
+// We use -6 for display, but we may want to split this into urgent (-8) and
+// non-urgent (-4).
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+    {kThreadPriority_RealtimeAudio, -16},
+    {kThreadPriority_Background, 10},
+    {kThreadPriority_Normal, 0},
+    {kThreadPriority_Display, -6},
+};
+
+bool HandleSetThreadPriorityForPlatform(PlatformThreadHandle handle,
+                                        ThreadPriority priority) {
   // On Android, we set the Audio priority through JNI as Audio priority
   // will also allow the process to run while it is backgrounded.
   if (priority == kThreadPriority_RealtimeAudio) {
     JNIEnv* env = base::android::AttachCurrentThread();
     Java_ThreadUtils_setThreadPriorityAudio(env, PlatformThread::CurrentId());
-    return;
+    return true;
   }
-
-  // setpriority(2) should change the whole thread group's (i.e. process)
-  // priority. however, on linux it will only change the target thread's
-  // priority. see the bugs section in
-  // http://man7.org/linux/man-pages/man2/getpriority.2.html.
-  // we prefer using 0 rather than the current thread id since they are
-  // equivalent but it makes sandboxing easier (https://crbug.com/399473).
-  DCHECK_NE(handle.id_, kInvalidThreadId);
-  int kNiceSetting = ThreadNiceValue(priority);
-  const PlatformThreadId current_id = PlatformThread::CurrentId();
-  if (setpriority(PRIO_PROCESS,
-                  handle.id_ == current_id ? 0 : handle.id_,
-                  kNiceSetting)) {
-    LOG(ERROR) << "Failed to set nice value of thread to " << kNiceSetting;
-  }
+  return false;
 }
 
+}  // namespace internal
+
 void PlatformThread::SetName(const char* name) {
   ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
   tracked_objects::ThreadData::InitializeThreadContext(name);
diff --git a/base/threading/platform_thread_freebsd.cc b/base/threading/platform_thread_freebsd.cc
index 7a24f4e..a163f65 100644
--- a/base/threading/platform_thread_freebsd.cc
+++ b/base/threading/platform_thread_freebsd.cc
@@ -9,39 +9,44 @@
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/safe_strerror_posix.h"
 #include "base/threading/thread_id_name_manager.h"
-#include "base/threading/thread_restrictions.h"
 #include "base/tracked_objects.h"
 
 #if !defined(OS_NACL)
-#include <sys/resource.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
+#include <pthread.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 #include <unistd.h>
 #endif
 
 namespace base {
 
+namespace internal {
+
 namespace {
-int ThreadNiceValue(ThreadPriority priority) {
-  switch (priority) {
-    case kThreadPriority_RealtimeAudio:
-      return -10;
-    case kThreadPriority_Background:
-      return 10;
-    case kThreadPriority_Normal:
-      return 0;
-    case kThreadPriority_Display:
-      return -6;
-    default:
-      NOTREACHED() << "Unknown priority.";
-      return 0;
-  }
+#if !defined(OS_NACL)
+const struct sched_param kRealTimePrio = {8};
+#endif
+}  // namespace
+
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+  { kThreadPriority_RealtimeAudio, -10 },
+  { kThreadPriority_Background, 10 },
+  { kThreadPriority_Normal, 0 },
+  { kThreadPriority_Display, -6 },
 }
-} // namespace
+
+bool HandleSetThreadPriorityForPlatform(PlatformThreadHandle handle,
+                                        ThreadPriority priority) {
+#if !defined(OS_NACL)
+  return priority == kThreadPriority_RealtimeAudio &&
+         pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
+#else
+  return false;
+#endif
+}
+
+}  // namespace internal
 
 // static
 void PlatformThread::SetName(const char* name) {
@@ -59,31 +64,6 @@
 #endif  //  !defined(OS_NACL)
 }
 
-// static
-void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
-                                       ThreadPriority priority) {
-#if !defined(OS_NACL)
-  if (priority == kThreadPriority_RealtimeAudio) {
-    const struct sched_param kRealTimePrio = { 8 };
-    if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) {
-      // Got real time priority, no need to set nice level.
-      return;
-    }
-  }
-
-  // setpriority(2) will set a thread's priority if it is passed a tid as
-  // the 'process identifier', not affecting the rest of the threads in the
-  // process. Setting this priority will only succeed if the user has been
-  // granted permission to adjust nice values on the system.
-  DCHECK_NE(handle.id_, kInvalidThreadId);
-  const int kNiceSetting = ThreadNiceValue(priority);
-  if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting)) {
-    DVPLOG(1) << "Failed to set nice value of thread ("
-              << handle.id_ << ") to " << kNiceSetting;
-  }
-#endif  //  !defined(OS_NACL)
-}
-
 void InitThreading() {}
 
 void InitOnThread() {}
diff --git a/base/threading/platform_thread_internal_posix.cc b/base/threading/platform_thread_internal_posix.cc
new file mode 100644
index 0000000..7e6604b
--- /dev/null
+++ b/base/threading/platform_thread_internal_posix.cc
@@ -0,0 +1,25 @@
+// 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/threading/platform_thread_internal_posix.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+int ThreadPriorityToNiceValue(ThreadPriority priority) {
+  for (const ThreadPriorityToNiceValuePair& pair :
+       kThreadPriorityToNiceValueMap) {
+    if (pair.priority == priority)
+      return pair.nice_value;
+  }
+  NOTREACHED() << "Unknown ThreadPriority";
+  return 0;
+}
+
+}  // namespace internal
+
+}  // namespace base
diff --git a/base/threading/platform_thread_internal_posix.h b/base/threading/platform_thread_internal_posix.h
new file mode 100644
index 0000000..1ae968f
--- /dev/null
+++ b/base/threading/platform_thread_internal_posix.h
@@ -0,0 +1,35 @@
+// 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_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
+#define BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
+
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace internal {
+
+struct ThreadPriorityToNiceValuePair {
+  ThreadPriority priority;
+  int nice_value;
+};
+extern const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4];
+
+// Returns the nice value matching |priority| based on the platform-specific
+// implementation of kThreadPriorityToNiceValueMap.
+int ThreadPriorityToNiceValue(ThreadPriority priority);
+
+// Allows platform specific tweaks to the generic POSIX solution for
+// SetThreadPriority. Returns true if the platform-specific implementation
+// handled this |priority| change, false if the generic implementation should
+// instead proceed.
+bool HandleSetThreadPriorityForPlatform(PlatformThreadHandle handle,
+                                        ThreadPriority priority);
+
+}  // namespace internal
+
+}  // namespace base
+
+#endif  // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
index d97a3f4..623a3de 100644
--- a/base/threading/platform_thread_linux.cc
+++ b/base/threading/platform_thread_linux.cc
@@ -9,44 +9,46 @@
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/safe_strerror_posix.h"
+#include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/thread_id_name_manager.h"
-#include "base/threading/thread_restrictions.h"
 #include "base/tracked_objects.h"
 
 #if !defined(OS_NACL)
+#include <pthread.h>
 #include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
+#include <sys/types.h>
 #include <unistd.h>
 #endif
 
 namespace base {
 
+namespace internal {
+
 namespace {
-
 #if !defined(OS_NACL)
-int ThreadNiceValue(ThreadPriority priority) {
-  switch (priority) {
-    case kThreadPriority_RealtimeAudio:
-      return -10;
-    case kThreadPriority_Background:
-      return 10;
-    case kThreadPriority_Normal:
-      return 0;
-    case kThreadPriority_Display:
-      return -6;
-    default:
-      NOTREACHED() << "Unknown priority.";
-      return 0;
-  }
-}
-#endif  // !defined(OS_NACL)
-
+const struct sched_param kRealTimePrio = {8};
+#endif
 }  // namespace
 
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+    {kThreadPriority_RealtimeAudio, -10},
+    {kThreadPriority_Background, 10},
+    {kThreadPriority_Normal, 0},
+    {kThreadPriority_Display, -6},
+};
+
+bool HandleSetThreadPriorityForPlatform(PlatformThreadHandle handle,
+                                        ThreadPriority priority) {
+#if !defined(OS_NACL)
+  return priority == kThreadPriority_RealtimeAudio &&
+         pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
+#else
+  return false;
+#endif
+}
+
+}  // namespace internal
+
 // static
 void PlatformThread::SetName(const char* name) {
   ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
@@ -72,36 +74,6 @@
 #endif  //  !defined(OS_NACL)
 }
 
-// static
-void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
-                                       ThreadPriority priority) {
-#if !defined(OS_NACL)
-  if (priority == kThreadPriority_RealtimeAudio) {
-    const struct sched_param kRealTimePrio = {8};
-    if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) {
-      // Got real time priority, no need to set nice level.
-      return;
-    }
-  }
-
-  // setpriority(2) should change the whole thread group's (i.e. process)
-  // priority. however, on linux it will only change the target thread's
-  // priority. see the bugs section in
-  // http://man7.org/linux/man-pages/man2/getpriority.2.html.
-  // we prefer using 0 rather than the current thread id since they are
-  // equivalent but it makes sandboxing easier (https://crbug.com/399473).
-  DCHECK_NE(handle.id_, kInvalidThreadId);
-  const int kNiceSetting = ThreadNiceValue(priority);
-  const PlatformThreadId current_id = PlatformThread::CurrentId();
-  if (setpriority(PRIO_PROCESS,
-                  handle.id_ == current_id ? 0 : handle.id_,
-                  kNiceSetting)) {
-    DVPLOG(1) << "Failed to set nice value of thread (" << handle.id_ << ") to "
-              << kNiceSetting;
-  }
-#endif  //  !defined(OS_NACL)
-}
-
 void InitThreading() {}
 
 void InitOnThread() {}
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index 42b416d..5c5023b 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -5,28 +5,25 @@
 #include "base/threading/platform_thread.h"
 
 #include <errno.h>
+#include <pthread.h>
 #include <sched.h>
+#include <sys/resource.h>
+#include <sys/time.h>
 
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/safe_strerror_posix.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread_internal_posix.h"
 #include "base/threading/thread_id_name_manager.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/tracked_objects.h"
 
-#if defined(OS_MACOSX)
-#include <sys/resource.h>
-#include <algorithm>
-#endif
-
 #if defined(OS_LINUX)
-#include <sys/prctl.h>
-#include <sys/resource.h>
 #include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
+#elif defined(OS_ANDROID)
+#include <sys/types.h>
 #endif
 
 namespace base {
@@ -235,4 +232,31 @@
   CHECK_EQ(0, pthread_join(thread_handle.handle_, NULL));
 }
 
+// Mac has its own SetThreadPriority() implementation.
+#if !defined(OS_MACOSX)
+// static
+void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
+                                       ThreadPriority priority) {
+#if !defined(OS_NACL)
+  if (internal::HandleSetThreadPriorityForPlatform(handle, priority))
+    return;
+
+  // setpriority(2) should change the whole thread group's (i.e. process)
+  // priority. However, as stated in the bugs section of
+  // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current
+  // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread
+  // attribute". Also, 0 is prefered to the current thread id since it is
+  // equivalent but makes sandboxing easier (https://crbug.com/399473).
+  DCHECK_NE(handle.id_, kInvalidThreadId);
+  const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
+  const PlatformThreadId current_id = PlatformThread::CurrentId();
+  if (setpriority(PRIO_PROCESS, handle.id_ == current_id ? 0 : handle.id_,
+                  nice_setting)) {
+    DVPLOG(1) << "Failed to set nice value of thread (" << handle.id_ << ") to "
+              << nice_setting;
+  }
+#endif  // !defined(OS_NACL)
+}
+#endif  // !defined(OS_MACOSX)
+
 }  // namespace base
diff --git a/base/time/time.cc b/base/time/time.cc
index 951f7ba..321323b 100644
--- a/base/time/time.cc
+++ b/base/time/time.cc
@@ -97,6 +97,33 @@
   return delta_;
 }
 
+int64 TimeDelta::SaturatedAdd(int64 value) const {
+  CheckedNumeric<int64> rv(delta_);
+  rv += value;
+  return FromCheckedNumeric(rv);
+}
+
+int64 TimeDelta::SaturatedSub(int64 value) const {
+  CheckedNumeric<int64> rv(delta_);
+  rv -= value;
+  return FromCheckedNumeric(rv);
+}
+
+// static
+int64 TimeDelta::FromCheckedNumeric(const CheckedNumeric<int64> value) {
+  if (value.IsValid())
+    return value.ValueUnsafe();
+
+  // We could return max/min but we don't really expose what the maximum delta
+  // is. Instead, return max/(-max), which is something that clients can reason
+  // about.
+  // TODO(rvargas) crbug.com/332611: don't use internal values.
+  int64 limit = std::numeric_limits<int64>::max();
+  if (value.validity() == internal::RANGE_UNDERFLOW)
+    limit = -limit;
+  return value.ValueOrDefault(limit);
+}
+
 std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
   return os << time_delta.InSecondsF() << "s";
 }
diff --git a/base/time/time.h b/base/time/time.h
index 6a45b81..5c8b89c 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -33,6 +33,7 @@
 
 #include "base/base_export.h"
 #include "base/basictypes.h"
+#include "base/numerics/safe_math.h"
 #include "build/build_config.h"
 
 #if defined(OS_MACOSX)
@@ -140,18 +141,18 @@
 
   // Computations with other deltas.
   TimeDelta operator+(TimeDelta other) const {
-    return TimeDelta(delta_ + other.delta_);
+    return TimeDelta(SaturatedAdd(other.delta_));
   }
   TimeDelta operator-(TimeDelta other) const {
-    return TimeDelta(delta_ - other.delta_);
+    return TimeDelta(SaturatedSub(other.delta_));
   }
 
   TimeDelta& operator+=(TimeDelta other) {
-    delta_ += other.delta_;
+    delta_ = SaturatedAdd(other.delta_);
     return *this;
   }
   TimeDelta& operator-=(TimeDelta other) {
-    delta_ -= other.delta_;
+    delta_ = SaturatedSub(other.delta_);
     return *this;
   }
   TimeDelta operator-() const {
@@ -161,20 +162,28 @@
   // Computations with numeric types.
   template<typename T>
   TimeDelta operator*(T a) const {
-    return TimeDelta(delta_ * a);
+    CheckedNumeric<int64> rv(delta_);
+    rv *= a;
+    return TimeDelta(FromCheckedNumeric(rv));
   }
   template<typename T>
   TimeDelta operator/(T a) const {
-    return TimeDelta(delta_ / a);
+    CheckedNumeric<int64> rv(delta_);
+    rv /= a;
+    return TimeDelta(FromCheckedNumeric(rv));
   }
   template<typename T>
   TimeDelta& operator*=(T a) {
-    delta_ *= a;
+    CheckedNumeric<int64> rv(delta_);
+    rv *= a;
+    delta_ = FromCheckedNumeric(rv);
     return *this;
   }
   template<typename T>
   TimeDelta& operator/=(T a) {
-    delta_ /= a;
+    CheckedNumeric<int64> rv(delta_);
+    rv /= a;
+    delta_ = FromCheckedNumeric(rv);
     return *this;
   }
 
@@ -216,6 +225,13 @@
   explicit TimeDelta(int64 delta_us) : delta_(delta_us) {
   }
 
+  // Add or subtract |value| from this delta.
+  int64 SaturatedAdd(int64 value) const;
+  int64 SaturatedSub(int64 value) const;
+
+  // Clamp |value| on overflow and underflow conditions.
+  static int64 FromCheckedNumeric(const CheckedNumeric<int64> value);
+
   // Delta in microseconds.
   int64 delta_;
 };
@@ -451,20 +467,20 @@
 
   // Modify by some time delta.
   Time& operator+=(TimeDelta delta) {
-    us_ += delta.delta_;
+    us_ = delta.SaturatedAdd(us_);
     return *this;
   }
   Time& operator-=(TimeDelta delta) {
-    us_ -= delta.delta_;
+    us_ = -delta.SaturatedSub(us_);
     return *this;
   }
 
   // Return a new time modified by some delta.
   Time operator+(TimeDelta delta) const {
-    return Time(us_ + delta.delta_);
+    return Time(delta.SaturatedAdd(us_));
   }
   Time operator-(TimeDelta delta) const {
-    return Time(us_ - delta.delta_);
+    return Time(-delta.SaturatedSub(us_));
   }
 
   // Comparison operators
@@ -583,7 +599,7 @@
 }
 
 inline Time TimeDelta::operator+(Time t) const {
-  return Time(t.us_ + delta_);
+  return Time(SaturatedAdd(t.us_));
 }
 
 // For logging use only.
@@ -665,6 +681,11 @@
     return ticks_ == 0;
   }
 
+  // Returns true if the time delta is the maximum delta.
+  bool is_max() const {
+    return ticks_ == std::numeric_limits<int64>::max();
+  }
+
   // Converts an integer value representing TimeTicks to a class. This is used
   // when deserializing a |TimeTicks| structure, using a value known to be
   // compatible. It is not provided as a constructor because the integer type
@@ -705,20 +726,20 @@
 
   // Modify by some time delta.
   TimeTicks& operator+=(TimeDelta delta) {
-    ticks_ += delta.delta_;
+    ticks_ = delta.SaturatedAdd(ticks_);
     return *this;
   }
   TimeTicks& operator-=(TimeDelta delta) {
-    ticks_ -= delta.delta_;
+    ticks_ = -delta.SaturatedSub(ticks_);
     return *this;
   }
 
   // Return a new TimeTicks modified by some delta.
   TimeTicks operator+(TimeDelta delta) const {
-    return TimeTicks(ticks_ + delta.delta_);
+    return TimeTicks(delta.SaturatedAdd(ticks_));
   }
   TimeTicks operator-(TimeDelta delta) const {
-    return TimeTicks(ticks_ - delta.delta_);
+    return TimeTicks(-delta.SaturatedSub(ticks_));
   }
 
   // Comparison operators
@@ -759,7 +780,7 @@
 };
 
 inline TimeTicks TimeDelta::operator+(TimeTicks t) const {
-  return TimeTicks(t.ticks_ + delta_);
+  return TimeTicks(SaturatedAdd(t.ticks_));
 }
 
 // For logging use only.
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index a96787c..6c12423 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -942,6 +942,77 @@
             2 * TimeDelta::FromMilliseconds(1000));
 }
 
+bool IsMin(TimeDelta delta) {
+  return (-delta).is_max();
+}
+
+TEST(TimeDelta, Overflows) {
+  // Some sanity checks.
+  EXPECT_TRUE(TimeDelta::Max().is_max());
+  EXPECT_TRUE(IsMin(-TimeDelta::Max()));
+  EXPECT_GT(TimeDelta(), -TimeDelta::Max());
+
+  TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1);
+  TimeDelta large_negative = -large_delta;
+  EXPECT_GT(TimeDelta(), large_negative);
+  EXPECT_FALSE(large_delta.is_max());
+  EXPECT_FALSE(IsMin(-large_negative));
+  TimeDelta one_second = TimeDelta::FromSeconds(1);
+
+  // Test +, -, * and / operators.
+  EXPECT_TRUE((large_delta + one_second).is_max());
+  EXPECT_TRUE(IsMin(large_negative + (-one_second)));
+  EXPECT_TRUE(IsMin(large_negative - one_second));
+  EXPECT_TRUE((large_delta - (-one_second)).is_max());
+  EXPECT_TRUE((large_delta * 2).is_max());
+  EXPECT_TRUE(IsMin(large_delta * -2));
+  EXPECT_TRUE((large_delta / 0.5).is_max());
+  EXPECT_TRUE(IsMin(large_delta / -0.5));
+
+  // Test +=, -=, *= and /= operators.
+  TimeDelta delta = large_delta;
+  delta += one_second;
+  EXPECT_TRUE(delta.is_max());
+  delta = large_negative;
+  delta += -one_second;
+  EXPECT_TRUE(IsMin(delta));
+
+  delta = large_negative;
+  delta -= one_second;
+  EXPECT_TRUE(IsMin(delta));
+  delta = large_delta;
+  delta -= -one_second;
+  EXPECT_TRUE(delta.is_max());
+
+  delta = large_delta;
+  delta *= 2;
+  EXPECT_TRUE(delta.is_max());
+  delta = large_negative;
+  delta *= 1.5;
+  EXPECT_TRUE(IsMin(delta));
+
+  delta = large_delta;
+  delta /= 0.5;
+  EXPECT_TRUE(delta.is_max());
+  delta = large_negative;
+  delta /= 0.5;
+  EXPECT_TRUE(IsMin(delta));
+
+  // Test operations with Time and TimeTicks.
+  EXPECT_TRUE((large_delta + Time::Now()).is_max());
+  EXPECT_TRUE((large_delta + TimeTicks::Now()).is_max());
+  EXPECT_TRUE((Time::Now() + large_delta).is_max());
+  EXPECT_TRUE((TimeTicks::Now() + large_delta).is_max());
+
+  Time time_now = Time::Now();
+  EXPECT_EQ(one_second, (time_now + one_second) - time_now);
+  EXPECT_EQ(-one_second, (time_now - one_second) - time_now);
+
+  TimeTicks ticks_now = TimeTicks::Now();
+  EXPECT_EQ(-one_second, (ticks_now - one_second) - ticks_now);
+  EXPECT_EQ(one_second, (ticks_now + one_second) - ticks_now);
+}
+
 TEST(TimeDeltaLogging, DCheckEqCompiles) {
   DCHECK_EQ(TimeDelta(), TimeDelta());
 }
diff --git a/base/trace_event/BUILD.gn b/base/trace_event/BUILD.gn
index 10f7ec9..479a918 100644
--- a/base/trace_event/BUILD.gn
+++ b/base/trace_event/BUILD.gn
@@ -1,11 +1,15 @@
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
+# 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.
 
 source_set("trace_event") {
   sources = [
+    "memory_allocator_attributes.h",
+    "memory_allocator_dump.cc",
+    "memory_allocator_dump.h",
     "memory_dump_manager.cc",
     "memory_dump_manager.h",
+    "memory_dump_provider.cc",
     "memory_dump_provider.h",
     "process_memory_dump.cc",
     "process_memory_dump.h",
@@ -59,3 +63,25 @@
 
   visibility = [ "//base/*" ]
 }
+
+source_set("trace_event_unittests") {
+  testonly = true
+  sources = [
+    "memory_allocator_dump_unittest.cc",
+    "memory_dump_manager_unittest.cc",
+    "process_memory_maps_dump_provider_unittest.cc",
+    "process_memory_totals_dump_provider_unittest.cc",
+    "trace_event_argument_unittest.cc",
+    "trace_event_memory_unittest.cc",
+    "trace_event_synthetic_delay_unittest.cc",
+    "trace_event_system_stats_monitor_unittest.cc",
+    "trace_event_unittest.cc",
+    "trace_event_win_unittest.cc",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/base/trace_event/memory_allocator_attributes.h b/base/trace_event/memory_allocator_attributes.h
new file mode 100644
index 0000000..efae9de
--- /dev/null
+++ b/base/trace_event/memory_allocator_attributes.h
@@ -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.
+
+#ifndef BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_H_
+#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/containers/hash_tables.h"
+
+namespace base {
+namespace trace_event {
+
+struct BASE_EXPORT MemoryAllocatorDeclaredAttribute {
+  std::string name;
+
+  // Refer to src/tools/perf/unit-info.json for the semantic of the type.
+  std::string type;
+};
+
+using MemoryAllocatorDeclaredAttributes =
+    hash_map<std::string, MemoryAllocatorDeclaredAttribute>;
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_H_
diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc
new file mode 100644
index 0000000..604af7a
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump.cc
@@ -0,0 +1,91 @@
+// 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/memory_allocator_dump.h"
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_allocator_attributes.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+MemoryAllocatorDump::MemoryAllocatorDump(const std::string& name,
+                                         MemoryAllocatorDump* parent)
+    : name_(name),
+      parent_(parent),
+      physical_size_in_bytes_(0),
+      allocated_objects_count_(0),
+      allocated_objects_size_in_bytes_(0) {
+  // Dots are not allowed in the name as the underlying base::DictionaryValue
+  // would treat them magically and split in sub-nodes, which is not intended.
+  DCHECK_EQ(std::string::npos, name.find_first_of('.'));
+}
+
+MemoryAllocatorDump::~MemoryAllocatorDump() {
+}
+
+void MemoryAllocatorDump::SetExtraAttribute(const std::string& name,
+                                            int value) {
+  extra_attributes_.SetInteger(name, value);
+}
+
+int MemoryAllocatorDump::GetExtraIntegerAttribute(
+    const std::string& name) const {
+  bool res;
+  int value = -1;
+  res = extra_attributes_.GetInteger(name, &value);
+  DCHECK(res) << "Allocator attribute '" << name << "' not found";
+  return value;
+}
+
+void MemoryAllocatorDump::AsValueInto(TracedValue* value) const {
+  static const char kHexFmt[] = "%" PRIx64;
+
+  value->BeginDictionary(name_.c_str());
+
+  value->SetString("parent", parent_ ? parent_->name_ : "");
+  value->SetString("physical_size_in_bytes",
+                   StringPrintf(kHexFmt, physical_size_in_bytes_));
+  value->SetString("allocated_objects_count",
+                   StringPrintf(kHexFmt, allocated_objects_count_));
+  value->SetString("allocated_objects_size_in_bytes",
+                   StringPrintf(kHexFmt, allocated_objects_size_in_bytes_));
+
+  // Copy all the extra attributes.
+  const MemoryDumpProvider* mdp =
+      MemoryDumpManager::GetInstance()->dump_provider_currently_active();
+  const MemoryAllocatorDeclaredAttributes& extra_attributes_types =
+      mdp->allocator_attributes();
+
+  value->BeginDictionary("args");
+  for (DictionaryValue::Iterator it(extra_attributes_); !it.IsAtEnd();
+       it.Advance()) {
+    const std::string& attr_name = it.key();
+    const Value& attr_value = it.value();
+    value->BeginDictionary(attr_name.c_str());
+    value->SetValue("value", attr_value.DeepCopy());
+
+    auto attr_it = extra_attributes_types.find(attr_name);
+    DCHECK(attr_it != extra_attributes_types.end())
+        << "Allocator attribute " << attr_name
+        << " not declared for the dumper " << mdp->GetFriendlyName();
+
+    // TODO(primiano): the "type" should be dumped just once, not repeated on
+    // on every event. The ability of doing so depends on crbug.com/466121.
+    value->SetString("type", attr_it->second.type);
+
+    value->EndDictionary();  // "arg_name": { "type": "...", "value": "..." }
+  }
+  value->EndDictionary();  // "args": {}
+
+  value->EndDictionary();  // "allocator name": {}
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
new file mode 100644
index 0000000..4d7293e
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump.h
@@ -0,0 +1,66 @@
+// 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_MEMORY_ALLOCATOR_DUMP_H_
+#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/trace_event/memory_allocator_attributes.h"
+#include "base/values.h"
+
+namespace base {
+namespace trace_event {
+
+class MemoryDumpManager;
+class TracedValue;
+
+// Data model for user-land memory allocator dumps.
+class BASE_EXPORT MemoryAllocatorDump {
+ public:
+  MemoryAllocatorDump(const std::string& name, MemoryAllocatorDump* parent);
+  ~MemoryAllocatorDump();
+
+  const std::string& name() const { return name_; }
+  const MemoryAllocatorDump* parent() const { return parent_; }
+
+  void set_physical_size_in_bytes(uint64 value) {
+    physical_size_in_bytes_ = value;
+  }
+  uint64 physical_size_in_bytes() const { return physical_size_in_bytes_; }
+
+  void set_allocated_objects_count(uint64 value) {
+    allocated_objects_count_ = value;
+  }
+  uint64 allocated_objects_count() const { return allocated_objects_count_; }
+
+  void set_allocated_objects_size_in_bytes(uint64 value) {
+    allocated_objects_size_in_bytes_ = value;
+  }
+  uint64 allocated_objects_size_in_bytes() const {
+    return allocated_objects_size_in_bytes_;
+  }
+
+  void SetExtraAttribute(const std::string& name, int value);
+  int GetExtraIntegerAttribute(const std::string& name) const;
+
+  // Called at trace generation time to populate the TracedValue.
+  void AsValueInto(TracedValue* value) const;
+
+ private:
+  const std::string name_;
+  MemoryAllocatorDump* const parent_;  // Not owned.
+  uint64 physical_size_in_bytes_;
+  uint64 allocated_objects_count_;
+  uint64 allocated_objects_size_in_bytes_;
+  DictionaryValue extra_attributes_;
+
+  DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorDump);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
new file mode 100644
index 0000000..8c14560
--- /dev/null
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -0,0 +1,95 @@
+// 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/memory_allocator_dump.h"
+
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+class FakeMemoryAllocatorDumpProvider : public MemoryDumpProvider {
+ public:
+  FakeMemoryAllocatorDumpProvider() {
+    DeclareAllocatorAttribute({"attr1", "count"});
+    DeclareAllocatorAttribute({"attr2", "bytes"});
+  }
+
+  bool DumpInto(ProcessMemoryDump* pmd) override {
+    MemoryAllocatorDump* mad_foo = pmd->CreateAllocatorDump("foo");
+    mad_foo->set_physical_size_in_bytes(4096);
+    mad_foo->set_allocated_objects_count(42);
+    mad_foo->set_allocated_objects_size_in_bytes(1000);
+    mad_foo->SetExtraAttribute("attr1", 1234);
+    mad_foo->SetExtraAttribute("attr2", 99);
+
+    MemoryAllocatorDump* mad_bar = pmd->CreateAllocatorDump("foo/bar", mad_foo);
+    mad_bar->set_physical_size_in_bytes(1);
+    mad_bar->set_allocated_objects_count(2);
+    mad_bar->set_allocated_objects_size_in_bytes(3);
+
+    pmd->CreateAllocatorDump("baz");
+    // Leave the rest of |baz| deliberately uninitialized, to check that
+    // CreateAllocatorDump returns a properly zero-initialized object.
+
+    return true;
+  }
+
+  const char* GetFriendlyName() const override { return "mock_allocator"; }
+};
+}  // namespace
+
+TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) {
+  FakeMemoryAllocatorDumpProvider fmadp;
+  ProcessMemoryDump pmd;
+
+  fmadp.DumpInto(&pmd);
+
+  ASSERT_EQ(3u, pmd.allocator_dumps().size());
+
+  const MemoryAllocatorDump* mad_foo = pmd.GetAllocatorDump("foo");
+  ASSERT_NE(nullptr, mad_foo);
+  EXPECT_EQ("foo", mad_foo->name());
+  ASSERT_EQ(nullptr, mad_foo->parent());
+  EXPECT_EQ(4096u, mad_foo->physical_size_in_bytes());
+  EXPECT_EQ(42u, mad_foo->allocated_objects_count());
+  EXPECT_EQ(1000u, mad_foo->allocated_objects_size_in_bytes());
+
+  // Check the extra attributes of |mad_foo|.
+  EXPECT_EQ(1234, mad_foo->GetExtraIntegerAttribute("attr1"));
+  EXPECT_EQ(99, mad_foo->GetExtraIntegerAttribute("attr2"));
+
+  const MemoryAllocatorDump* mad_bar = pmd.GetAllocatorDump("foo/bar");
+  ASSERT_NE(nullptr, mad_bar);
+  EXPECT_EQ("foo/bar", mad_bar->name());
+  ASSERT_EQ(mad_foo, mad_bar->parent());
+  EXPECT_EQ(1u, mad_bar->physical_size_in_bytes());
+  EXPECT_EQ(2u, mad_bar->allocated_objects_count());
+  EXPECT_EQ(3u, mad_bar->allocated_objects_size_in_bytes());
+
+  const MemoryAllocatorDump* mad_baz = pmd.GetAllocatorDump("baz");
+  ASSERT_NE(nullptr, mad_baz);
+  EXPECT_EQ("baz", mad_baz->name());
+  ASSERT_EQ(nullptr, mad_baz->parent());
+  EXPECT_EQ(0u, mad_baz->physical_size_in_bytes());
+  EXPECT_EQ(0u, mad_baz->allocated_objects_count());
+  EXPECT_EQ(0u, mad_baz->allocated_objects_size_in_bytes());
+}
+
+// DEATH tests are not supported in Android / iOS.
+#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
+TEST(MemoryAllocatorDumpTest, ForbidDuplicatesDeathTest) {
+  FakeMemoryAllocatorDumpProvider fmadp;
+  ProcessMemoryDump pmd;
+  pmd.CreateAllocatorDump("dump_1");
+  pmd.CreateAllocatorDump("dump_2");
+  ASSERT_DEATH(pmd.CreateAllocatorDump("dump_1"), "");
+}
+#endif
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 8b2d930..0ec5d19 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -59,7 +59,8 @@
   g_instance_for_testing = instance;
 }
 
-MemoryDumpManager::MemoryDumpManager() : memory_tracing_enabled_(0) {
+MemoryDumpManager::MemoryDumpManager()
+    : dump_provider_currently_active_(nullptr), memory_tracing_enabled_(0) {
 }
 
 MemoryDumpManager::~MemoryDumpManager() {
@@ -128,17 +129,19 @@
     AutoLock lock(lock_);
     for (auto it = dump_providers_enabled_.begin();
          it != dump_providers_enabled_.end();) {
-      MemoryDumpProvider* dump_provider = *it;
-      if (dump_provider->DumpInto(pmd.get())) {
+      dump_provider_currently_active_ = *it;
+      if (dump_provider_currently_active_->DumpInto(pmd.get())) {
         did_any_provider_dump = true;
         ++it;
       } else {
-        LOG(ERROR) << "The memory dumper " << dump_provider->GetFriendlyName()
+        LOG(ERROR) << "The memory dumper "
+                   << dump_provider_currently_active_->GetFriendlyName()
                    << " failed, possibly due to sandboxing (crbug.com/461788), "
                       "disabling it for current process. Try restarting chrome "
                       "with the --no-sandbox switch.";
         it = dump_providers_enabled_.erase(it);
       }
+      dump_provider_currently_active_ = nullptr;
     }
   }
 
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 8a9b3b7..04d0135 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -49,6 +49,12 @@
   void OnTraceLogEnabled() override;
   void OnTraceLogDisabled() override;
 
+  // Returns the MemoryDumpProvider which is currently being dumping into a
+  // ProcessMemoryDump via DumpInto(...) if any, nullptr otherwise.
+  MemoryDumpProvider* dump_provider_currently_active() const {
+    return dump_provider_currently_active_;
+  }
+
  private:
   friend struct DefaultDeleter<MemoryDumpManager>;  // For the testing instance.
   friend struct DefaultSingletonTraits<MemoryDumpManager>;
@@ -70,6 +76,9 @@
   std::vector<MemoryDumpProvider*> dump_providers_registered_;  // Not owned.
   std::vector<MemoryDumpProvider*> dump_providers_enabled_;     // Not owned.
 
+  // TODO(primiano): this is required only until crbug.com/466121 gets fixed.
+  MemoryDumpProvider* dump_provider_currently_active_;  // Now owned.
+
   // Protects from concurrent accesses to the |dump_providers_*|, e.g., tearing
   // down logging while creating a dump point on another thread.
   Lock lock_;
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 251a92a..65e719f 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
+using testing::Invoke;
 using testing::Return;
 
 namespace base {
@@ -51,6 +52,14 @@
  public:
   MOCK_METHOD1(DumpInto, bool(ProcessMemoryDump* pmd));
 
+  // DumpInto() override for the ActiveDumpProviderConsistency test,
+  bool DumpIntoAndCheckDumpProviderCurrentlyActive(ProcessMemoryDump* pmd) {
+    EXPECT_EQ(
+        this,
+        MemoryDumpManager::GetInstance()->dump_provider_currently_active());
+    return true;
+  }
+
   const char* GetFriendlyName() const override { return "MockDumpProvider"; }
 };
 
@@ -148,5 +157,30 @@
   DisableTracing();
 }
 
+// TODO(primiano): remove once crbug.com/466121 gets fixed.
+// Ascertains that calls to MDM::dump_provider_currently_active() actually
+// returns the MemoryDumpProvider currently active during the DumpInto() call.
+TEST_F(MemoryDumpManagerTest, ActiveDumpProviderConsistency) {
+  MockDumpProvider mdp1;
+  MockDumpProvider mdp2;
+
+  mdm_->RegisterDumpProvider(&mdp1);
+  mdm_->RegisterDumpProvider(&mdp2);
+  EnableTracing(kTraceCategory);
+  EXPECT_CALL(mdp1, DumpInto(_))
+      .Times(2)
+      .WillRepeatedly(Invoke(
+          &mdp1,
+          &MockDumpProvider::DumpIntoAndCheckDumpProviderCurrentlyActive));
+  EXPECT_CALL(mdp2, DumpInto(_))
+      .Times(2)
+      .WillRepeatedly(Invoke(
+          &mdp2,
+          &MockDumpProvider::DumpIntoAndCheckDumpProviderCurrentlyActive));
+  mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+  mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+  DisableTracing();
+}
+
 }  // namespace trace_event
 }  // namespace base
diff --git a/base/trace_event/memory_dump_provider.cc b/base/trace_event/memory_dump_provider.cc
new file mode 100644
index 0000000..9518461
--- /dev/null
+++ b/base/trace_event/memory_dump_provider.cc
@@ -0,0 +1,27 @@
+// 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/memory_dump_provider.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace trace_event {
+
+MemoryDumpProvider::MemoryDumpProvider() {
+}
+
+MemoryDumpProvider::~MemoryDumpProvider() {
+}
+
+void MemoryDumpProvider::DeclareAllocatorAttribute(
+    const MemoryAllocatorDeclaredAttribute& attr) {
+  DCHECK_EQ(0u, allocator_attributes_.count(attr.name))
+      << "Allocator attribute " << attr.name << " already declared for dumper "
+      << GetFriendlyName();
+  allocator_attributes_[attr.name] = attr;
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_dump_provider.h b/base/trace_event/memory_dump_provider.h
index 1c5bbb1..5cc3a6e 100644
--- a/base/trace_event/memory_dump_provider.h
+++ b/base/trace_event/memory_dump_provider.h
@@ -6,7 +6,7 @@
 #define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
 
 #include "base/base_export.h"
-#include "base/macros.h"
+#include "base/trace_event/memory_allocator_attributes.h"
 
 namespace base {
 namespace trace_event {
@@ -22,11 +22,22 @@
 
   virtual const char* GetFriendlyName() const = 0;
 
+  const MemoryAllocatorDeclaredAttributes& allocator_attributes() const {
+    return allocator_attributes_;
+  }
+
  protected:
-  MemoryDumpProvider() {}
-  virtual ~MemoryDumpProvider() {}
+  MemoryDumpProvider();
+  virtual ~MemoryDumpProvider();
+
+  void DeclareAllocatorAttribute(const MemoryAllocatorDeclaredAttribute& attr);
 
  private:
+  // The map (attribute name -> type) that specifies the semantic of the
+  // extra attributes that the MemoryAllocatorDump(s) produced by this
+  // MemoryDumpProvider will have.
+  MemoryAllocatorDeclaredAttributes allocator_attributes_;
+
   DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider);
 };
 
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 5363db5..bbca36c 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -17,6 +17,27 @@
 ProcessMemoryDump::~ProcessMemoryDump() {
 }
 
+MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
+    const std::string& name) {
+  return CreateAllocatorDump(name, nullptr);
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
+    const std::string& name,
+    MemoryAllocatorDump* parent) {
+  DCHECK_EQ(0ul, allocator_dumps_.count(name));
+  MemoryAllocatorDump* mad = new MemoryAllocatorDump(name, parent);
+  allocator_dumps_storage_.push_back(mad);
+  allocator_dumps_[name] = mad;
+  return mad;
+}
+
+MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
+    const std::string& name) const {
+  auto it = allocator_dumps_.find(name);
+  return it == allocator_dumps_.end() ? nullptr : it->second;
+}
+
 void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
   // Build up the [dumper name] -> [value] dictionary.
   if (has_process_totals_) {
@@ -29,6 +50,12 @@
     process_mmaps_.AsValueInto(value);
     value->EndDictionary();
   }
+  if (allocator_dumps_storage_.size() > 0) {
+    value->BeginDictionary("allocators");
+    for (const MemoryAllocatorDump* allocator_dump : allocator_dumps_storage_)
+      allocator_dump->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 4256c4c..df6cc82 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -6,6 +6,10 @@
 #define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
 
 #include "base/base_export.h"
+#include "base/containers/hash_tables.h"
+#include "base/containers/small_map.h"
+#include "base/memory/scoped_vector.h"
+#include "base/trace_event/memory_allocator_dump.h"
 #include "base/trace_event/process_memory_maps.h"
 #include "base/trace_event/process_memory_totals.h"
 
@@ -13,6 +17,7 @@
 namespace trace_event {
 
 class ConvertableToTraceFormat;
+class MemoryDumpManager;
 
 // ProcessMemoryDump is as a strongly typed container which enforces the data
 // model for each memory dump point and holds the dumps produced by the
@@ -22,6 +27,9 @@
 // dump point time.
 class BASE_EXPORT ProcessMemoryDump {
  public:
+  using AllocatorDumpsMap =
+      SmallMap<hash_map<std::string, MemoryAllocatorDump*>>;
+
   ProcessMemoryDump();
   ~ProcessMemoryDump();
 
@@ -36,6 +44,21 @@
   bool has_process_mmaps() const { return has_process_mmaps_; }
   void set_has_process_mmaps() { has_process_mmaps_ = true; }
 
+  // Creates a new MemoryAllocatorDump with the given name and returns the
+  // empty object back to the caller. The |name| must be unique in the dump.
+  // ProcessMemoryDump handles the memory ownership of the created object.
+  // |parent| can be used to specify a hierarchical relationship of the
+  // allocator dumps.
+  MemoryAllocatorDump* CreateAllocatorDump(const std::string& name);
+  MemoryAllocatorDump* CreateAllocatorDump(const std::string& name,
+                                           MemoryAllocatorDump* parent);
+
+  // Returns a MemoryAllocatorDump given its name or nullptr if not found.
+  MemoryAllocatorDump* GetAllocatorDump(const std::string& name) const;
+
+  // Returns the map of the MemoryAllocatorDumps added to this dump.
+  const AllocatorDumpsMap& allocator_dumps() const { return allocator_dumps_; }
+
  private:
   ProcessMemoryTotals process_totals_;
   bool has_process_totals_;
@@ -43,6 +66,13 @@
   ProcessMemoryMaps process_mmaps_;
   bool has_process_mmaps_;
 
+  // A maps of "allocator_name" -> MemoryAllocatorDump populated by
+  // allocator dump providers.
+  AllocatorDumpsMap allocator_dumps_;
+
+  // ProcessMemoryDump handles the memory ownership of all its belongings.
+  ScopedVector<MemoryAllocatorDump> allocator_dumps_storage_;
+
   DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
 };
 
diff --git a/base/trace_event/process_memory_maps.cc b/base/trace_event/process_memory_maps.cc
index bf3c5a0..d553ee8 100644
--- a/base/trace_event/process_memory_maps.cc
+++ b/base/trace_event/process_memory_maps.cc
@@ -4,6 +4,8 @@
 
 #include "base/trace_event/process_memory_maps.h"
 
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event_argument.h"
 
 namespace base {
@@ -21,19 +23,25 @@
 }
 
 void ProcessMemoryMaps::AsValueInto(TracedValue* value) const {
+  static const char kHexFmt[] = "%" PRIx64;
+
+  // Refer to the design doc goo.gl/sxfFY8 for the semantic of these fields.
   value->BeginArray("vm_regions");
   for (const auto& region : vm_regions_) {
     value->BeginDictionary();
 
-    value->SetDouble("start_address", region.start_address);
-    value->SetDouble("size_in_bytes", region.size_in_bytes);
-    value->SetInteger("protection_flags", region.protection_flags);
-    value->SetString("mapped_file", region.mapped_file);
-    value->SetDouble("mapped_file_offset", region.mapped_file_offset);
+    value->SetString("sa", StringPrintf(kHexFmt, region.start_address));
+    value->SetString("sz", StringPrintf(kHexFmt, region.size_in_bytes));
+    value->SetInteger("pf", region.protection_flags);
+    value->SetString("mf", region.mapped_file);
 
-    value->BeginDictionary("byte_stats");
-    value->SetDouble("resident", region.byte_stats_resident);
-    value->SetDouble("anonymous", region.byte_stats_anonymous);
+    value->BeginDictionary("bs");  // byte stats
+    value->SetString(
+        "pss", StringPrintf(kHexFmt, region.byte_stats_proportional_resident));
+    value->SetString("prv",
+                     StringPrintf(kHexFmt, region.byte_stats_private_resident));
+    value->SetString("shr",
+                     StringPrintf(kHexFmt, region.byte_stats_shared_resident));
     value->EndDictionary();
 
     value->EndDictionary();
diff --git a/base/trace_event/process_memory_maps.h b/base/trace_event/process_memory_maps.h
index 70f6610..dc1892f 100644
--- a/base/trace_event/process_memory_maps.h
+++ b/base/trace_event/process_memory_maps.h
@@ -28,9 +28,13 @@
     uint64 size_in_bytes;
     uint32 protection_flags;
     std::string mapped_file;
-    uint64 mapped_file_offset;
-    uint64 byte_stats_resident;
-    uint64 byte_stats_anonymous;
+
+    // private_resident + shared_resident = resident set size.
+    uint64 byte_stats_private_resident;
+    uint64 byte_stats_shared_resident;
+
+    // For multiprocess accounting.
+    uint64 byte_stats_proportional_resident;
   };
 
   ProcessMemoryMaps();
diff --git a/base/trace_event/process_memory_maps_dump_provider.cc b/base/trace_event/process_memory_maps_dump_provider.cc
index fbe0eb1..d728bd3 100644
--- a/base/trace_event/process_memory_maps_dump_provider.cc
+++ b/base/trace_event/process_memory_maps_dump_provider.cc
@@ -60,7 +60,7 @@
     region->protection_flags |=
         ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
   }
-  *smaps >> std::hex >> region->mapped_file_offset;
+  *smaps >> ignored;  // Ignore mapped file offset.
   *smaps >> ignored;  // Ignore device maj-min (fc:01 in the example above).
   *smaps >> ignored;  // Ignore inode number (1234 in the example above).
 
@@ -73,9 +73,15 @@
   return res;
 }
 
+uint64 ReadCounterBytes(std::istream* smaps) {
+  uint64 counter_value = 0;
+  *smaps >> std::dec >> counter_value;
+  return counter_value * 1024;
+}
+
 uint32 ParseSmapsCounter(std::istream* smaps,
                          ProcessMemoryMaps::VMRegion* region) {
-  // e.g., "RSS:  0 Kb\n"
+  // A smaps counter lines looks as follows: "RSS:  0 Kb\n"
   uint32 res = 0;
   std::string counter_name;
   *smaps >> counter_name;
@@ -83,13 +89,17 @@
   // TODO(primiano): "Swap" should also be accounted as resident. Check
   // whether Rss isn't already counting swapped and fix below if that is
   // the case.
-  if (counter_name == "Rss:") {
-    *smaps >> std::dec >> region->byte_stats_resident;
-    region->byte_stats_resident *= 1024;
+  if (counter_name == "Pss:") {
+    region->byte_stats_proportional_resident = ReadCounterBytes(smaps);
     res = 1;
-  } else if (counter_name == "Anonymous:") {
-    *smaps >> std::dec >> region->byte_stats_anonymous;
-    region->byte_stats_anonymous *= 1024;
+  } else if (counter_name == "Private_Dirty:" ||
+             counter_name == "Private_Clean:") {
+    // For Private and Shared counters keep the sum of the dirty + clean stats.
+    region->byte_stats_private_resident += ReadCounterBytes(smaps);
+    res = 1;
+  } else if (counter_name == "Shared_Dirty:" ||
+             counter_name == "Shared_Clean:") {
+    region->byte_stats_shared_resident += ReadCounterBytes(smaps);
     res = 1;
   }
 
@@ -111,7 +121,7 @@
   if (!smaps->good())
     return 0;
 
-  const uint32 kNumExpectedCountersPerRegion = 2;
+  const uint32 kNumExpectedCountersPerRegion = 5;
   uint32 counters_parsed_for_current_region = 0;
   uint32 num_valid_regions = 0;
   ProcessMemoryMaps::VMRegion region;
diff --git a/base/trace_event/process_memory_maps_dump_provider_unittest.cc b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
index 02fd136..0bf81ac 100644
--- a/base/trace_event/process_memory_maps_dump_provider_unittest.cc
+++ b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
@@ -36,12 +36,12 @@
     "VmFlags: rd ex mr mw me dw sd\n"
     "ff000000-ff800000 -w-p 00001080 fc:01 0            /file/name with space\n"
     "Size:                  0 kB\n"
-    "Rss:                 128 kB\n"
+    "Rss:                 192 kB\n"
     "Pss:                 128 kB\n"
-    "Shared_Clean:        124 kB\n"
-    "Shared_Dirty:          0 kB\n"
-    "Private_Clean:        68 kB\n"
-    "Private_Dirty:         0 kB\n"
+    "Shared_Clean:        120 kB\n"
+    "Shared_Dirty:          4 kB\n"
+    "Private_Clean:        60 kB\n"
+    "Private_Dirty:         8 kB\n"
     "Referenced:          296 kB\n"
     "Anonymous:             0 kB\n"
     "AnonHugePages:         0 kB\n"
@@ -91,7 +91,7 @@
     "7fe7ce79c000-7fe7ce7a8000 ---p 00000000 00:00 0 \n"
     "Size:                 48 kB\n"
     "Rss:                  40 kB\n"
-    "Pss:                   0 kB\n"
+    "Pss:                  32 kB\n"
     "Shared_Clean:         16 kB\n"
     "Shared_Dirty:         12 kB\n"
     "Private_Clean:         8 kB\n"
@@ -141,17 +141,17 @@
   EXPECT_EQ(0x004be000UL - 0x00400000UL, regions_1[0].size_in_bytes);
   EXPECT_EQ(kProtR | kProtX, regions_1[0].protection_flags);
   EXPECT_EQ("/file/1", regions_1[0].mapped_file);
-  EXPECT_EQ(0UL, regions_1[0].mapped_file_offset);
-  EXPECT_EQ(296 * 1024UL, regions_1[0].byte_stats_resident);
-  EXPECT_EQ(68 * 1024UL, regions_1[0].byte_stats_anonymous);
+  EXPECT_EQ(162 * 1024UL, regions_1[0].byte_stats_proportional_resident);
+  EXPECT_EQ((228 + 0) * 1024UL, regions_1[0].byte_stats_shared_resident);
+  EXPECT_EQ((0 + 68) * 1024UL, regions_1[0].byte_stats_private_resident);
 
   EXPECT_EQ(0xff000000UL, regions_1[1].start_address);
   EXPECT_EQ(0xff800000UL - 0xff000000UL, regions_1[1].size_in_bytes);
   EXPECT_EQ(kProtW, regions_1[1].protection_flags);
   EXPECT_EQ("/file/name with space", regions_1[1].mapped_file);
-  EXPECT_EQ(0x00001080UL, regions_1[1].mapped_file_offset);
-  EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_resident);
-  EXPECT_EQ(0UL, regions_1[1].byte_stats_anonymous);
+  EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_proportional_resident);
+  EXPECT_EQ((120 + 4) * 1024UL, regions_1[1].byte_stats_shared_resident);
+  EXPECT_EQ((60 + 8) * 1024UL, regions_1[1].byte_stats_private_resident);
 
   // Parse the 2nd smaps file.
   ProcessMemoryDump pmd_2;
@@ -165,9 +165,9 @@
   EXPECT_EQ(0x7fe7ce7a8000UL - 0x7fe7ce79c000UL, regions_2[0].size_in_bytes);
   EXPECT_EQ(0U, regions_2[0].protection_flags);
   EXPECT_EQ("", regions_2[0].mapped_file);
-  EXPECT_EQ(0UL, regions_2[0].mapped_file_offset);
-  EXPECT_EQ(40 * 1024UL, regions_2[0].byte_stats_resident);
-  EXPECT_EQ(16 * 1024UL, regions_2[0].byte_stats_anonymous);
+  EXPECT_EQ(32 * 1024UL, regions_2[0].byte_stats_proportional_resident);
+  EXPECT_EQ((16 + 12) * 1024UL, regions_2[0].byte_stats_shared_resident);
+  EXPECT_EQ((8 + 4) * 1024UL, regions_2[0].byte_stats_private_resident);
 }
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
diff --git a/base/trace_event/process_memory_totals.cc b/base/trace_event/process_memory_totals.cc
index 41ad788..9b0c377 100644
--- a/base/trace_event/process_memory_totals.cc
+++ b/base/trace_event/process_memory_totals.cc
@@ -4,13 +4,16 @@
 
 #include "base/trace_event/process_memory_totals.h"
 
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.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_);
+  value->SetString("resident_set_bytes",
+                   StringPrintf("%" PRIx64, resident_set_bytes_));
 }
 
 }  // namespace trace_event
diff --git a/base/trace_event/process_memory_totals.h b/base/trace_event/process_memory_totals.h
index 1c99152..29c94c9 100644
--- a/base/trace_event/process_memory_totals.h
+++ b/base/trace_event/process_memory_totals.h
@@ -13,10 +13,10 @@
 
 class TracedValue;
 
-// Dump provider which collects process-wide memory stats.
+// Data model for process-wide memory stats.
 class BASE_EXPORT ProcessMemoryTotals {
  public:
-  ProcessMemoryTotals() {}
+  ProcessMemoryTotals() : resident_set_bytes_(0) {}
 
   // Called at trace generation time to populate the TracedValue.
   void AsValueInto(TracedValue* value) const;
diff --git a/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
index c37e612..372db63 100644
--- a/base/trace_event/process_memory_totals_dump_provider_unittest.cc
+++ b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
@@ -12,18 +12,18 @@
 namespace trace_event {
 
 TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
-  auto mdptp = ProcessMemoryTotalsDumpProvider::GetInstance();
+  auto pmtdp = 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());
+  pmtdp->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());
+  pmtdp->DumpInto(pmd_after.get());
 
   ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
 
diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi
new file mode 100644
index 0000000..ca6c076
--- /dev/null
+++ b/base/trace_event/trace_event.gypi
@@ -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.
+{
+  'variables': {
+    'trace_event_sources' : [
+      'trace_event/memory_allocator_attributes.h',
+      'trace_event/memory_allocator_dump.cc',
+      'trace_event/memory_allocator_dump.h',
+      'trace_event/memory_dump_manager.cc',
+      'trace_event/memory_dump_manager.h',
+      'trace_event/memory_dump_provider.cc',
+      'trace_event/memory_dump_provider.h',
+      'trace_event/process_memory_dump.cc',
+      'trace_event/process_memory_dump.h',
+      'trace_event/process_memory_maps.cc',
+      'trace_event/process_memory_maps.h',
+      'trace_event/process_memory_maps_dump_provider.cc',
+      'trace_event/process_memory_maps_dump_provider.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',
+      'trace_event/trace_event_argument.h',
+      'trace_event/trace_event_impl.cc',
+      'trace_event/trace_event_impl.h',
+      'trace_event/trace_event_impl_constants.cc',
+      'trace_event/trace_event_memory.cc',
+      'trace_event/trace_event_memory.h',
+      'trace_event/trace_event_synthetic_delay.cc',
+      'trace_event/trace_event_synthetic_delay.h',
+      'trace_event/trace_event_system_stats_monitor.cc',
+      'trace_event/trace_event_system_stats_monitor.h',
+      'trace_event/trace_event_win.cc',
+      'trace_event/trace_event_win.h',
+    ],
+    'trace_event_test_sources' : [
+      'trace_event/memory_allocator_dump_unittest.cc',
+      'trace_event/memory_dump_manager_unittest.cc',
+      'trace_event/process_memory_maps_dump_provider_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',
+      'trace_event/trace_event_system_stats_monitor_unittest.cc',
+      'trace_event/trace_event_unittest.cc',
+      'trace_event/trace_event_win_unittest.cc',
+    ],
+  },
+}
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index 9d8e7d1..1bf9429 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -681,16 +681,20 @@
 // events.
 // - category and name strings must have application lifetime (statics or
 //   literals). They may not include " chars.
-// - |id| is used to match the NESTABLE_ASYNC_BEGIN event with the
-//   NESTABLE_ASYNC_END event. Events are considered to match if their
-//   category_group, name and id values all match. |id| must either be a
-//   pointer or an integer value up to 64 bits. If it's a pointer, the bits
-//   will be xored with a hash of the process ID so that the same pointer on two
-//   different processes will not collide.
+// - A pair of NESTABLE_ASYNC_BEGIN event and NESTABLE_ASYNC_END event is
+//   considered as a match if their category_group, name and id all match.
+// - |id| must either be a pointer or an integer value up to 64 bits.
+//   If it's a pointer, the bits will be xored with a hash of the process ID so
+//   that the same pointer on two different processes will not collide.
+// - |id| is used to match a child NESTABLE_ASYNC event with its parent
+//   NESTABLE_ASYNC event. Therefore, events in the same nested event tree must
+//   be logged using the same id and category_group.
 //
-// Unmatched NESTABLE_ASYNC_END event will be parsed as an instant event,
-// and unmatched NESTABLE_ASYNC_BEGIN event will be parsed as an event that
-// ends at the last NESTABLE_ASYNC_END event of that |id|.
+// Unmatched NESTABLE_ASYNC_END event will be parsed as an event that starts
+// at the first NESTABLE_ASYNC event of that id, and unmatched
+// NESTABLE_ASYNC_BEGIN event will be parsed as an event that ends at the last
+// NESTABLE_ASYNC event of that id. Corresponding warning messages for
+// unmatched events will be shown in the analysis view.
 
 // Records a single NESTABLE_ASYNC_BEGIN event called "name" immediately, with 2
 // associated arguments. If the category is not enabled, then this does nothing.
diff --git a/build/android/adb_gdb b/build/android/adb_gdb
index c71b816..3612f90 100755
--- a/build/android/adb_gdb
+++ b/build/android/adb_gdb
@@ -97,6 +97,8 @@
 NO_PULL_LIBS=
 PACKAGE_NAME=
 PID=
+PRIVILEGED=
+PRIVILEGED_INDEX=
 PROGRAM_NAME="activity"
 PULL_LIBS=
 PULL_LIBS_DIR=
@@ -145,6 +147,13 @@
     --pid=*)
       PID=$optarg
       ;;
+    --privileged)
+      PRIVILEGED=true
+      ;;
+    --privileged=*)
+      PRIVILEGED=true
+      PRIVILEGED_INDEX=$optarg
+      ;;
     --program-name=*)
       PROGRAM_NAME=$optarg
       ;;
@@ -236,8 +245,9 @@
   cat <<EOF
 
 This script is used to debug a running $PROGRAM_NAME process.
-This can be a regular Android application process, or a sandboxed
-service, if you use the --sandboxed or --sandboxed=<num> option.
+This can be a regular Android application process, sandboxed (if you use the
+--sandboxed or --sandboxed=<num> option) or a privileged (--privileged or
+--privileged=<num>) service.
 
 This script needs several things to work properly. It will try to pick
 them up automatically for you though:
@@ -304,6 +314,8 @@
   --symbol-dir=<path>   Specify directory with symbol shared libraries.
   --out-dir=<path>      Specify the out directory.
   --package-name=<name> Specify package name (alternative to 1st argument).
+  --privileged          Debug first privileged process we find.
+  --privileged=<num>    Debug specific privileged process.
   --program-name=<name> Specify program name (cosmetic only).
   --pid=<pid>           Specify application process pid.
   --force               Kill any previous debugging session, if any.
@@ -818,6 +830,12 @@
     PROCESSNAME=$PROCESSNAME:sandboxed_process
     PID=$(adb_shell ps | \
           awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1)
+  elif [ "$PRIVILEGED_INDEX" ]; then
+    PROCESSNAME=$PROCESSNAME:privileged_process$PRIVILEGED_INDEX
+  elif [ "$PRIVILEGED" ]; then
+    PROCESSNAME=$PROCESSNAME:privileged_process
+    PID=$(adb_shell ps | \
+          awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1)
   fi
   if [ -z "$PID" ]; then
     PID=$(adb_shell ps | \
@@ -834,6 +852,8 @@
   log "Found process PID: $PID"
 elif [ "$SANDBOXED" ]; then
   echo "WARNING: --sandboxed option ignored due to use of --pid."
+elif [ "$PRIVILEGED" ]; then
+  echo "WARNING: --privileged option ignored due to use of --pid."
 fi
 
 # Determine if 'adb shell' runs as root or not.
diff --git a/build/android/gyp/generate_v14_compatible_resources.py b/build/android/gyp/generate_v14_compatible_resources.py
index 79a2d5f..7818170 100755
--- a/build/android/gyp/generate_v14_compatible_resources.py
+++ b/build/android/gyp/generate_v14_compatible_resources.py
@@ -11,7 +11,7 @@
 
 1. paddingStart attribute can cause a crash on Galaxy Tab 2.
 2. There is a bug that paddingStart does not override paddingLeft on
-   JB-MR1. This is fixed on JB-MR2.
+   JB-MR1. This is fixed on JB-MR2. b/8654490
 
 Therefore, this resource generation script can be removed when
 we drop the support for JB-MR1.
diff --git a/build/android/gyp/jinja_template.py b/build/android/gyp/jinja_template.py
index 3a93f74..9e9705c 100755
--- a/build/android/gyp/jinja_template.py
+++ b/build/android/gyp/jinja_template.py
@@ -18,18 +18,31 @@
 import jinja2  # pylint: disable=F0401
 
 
-def ProcessFile(input_filename, output_filename, variables):
-  with codecs.open(input_filename, 'r', 'utf-8') as input_file:
-    input_ = input_file.read()
-  env = jinja2.Environment(undefined=jinja2.StrictUndefined)
-  template = env.from_string(input_)
-  template.filename = os.path.abspath(input_filename)
+class RecordingFileSystemLoader(jinja2.FileSystemLoader):
+  '''A FileSystemLoader that stores a list of loaded templates.'''
+  def __init__(self, searchpath):
+    jinja2.FileSystemLoader.__init__(self, searchpath)
+    self.loaded_templates = set()
+
+  def get_source(self, environment, template):
+    contents, filename, uptodate = jinja2.FileSystemLoader.get_source(
+        self, environment, template)
+    self.loaded_templates.add(os.path.relpath(filename))
+    return contents, filename, uptodate
+
+  def get_loaded_templates(self):
+    return list(self.loaded_templates)
+
+
+def ProcessFile(env, input_filename, output_filename, variables):
+  input_rel_path = os.path.relpath(input_filename, build_utils.CHROMIUM_SRC)
+  template = env.get_template(input_rel_path)
   output = template.render(variables)
   with codecs.open(output_filename, 'w', 'utf-8') as output_file:
     output_file.write(output)
 
 
-def ProcessFiles(input_filenames, inputs_base_dir, outputs_zip, variables):
+def ProcessFiles(env, input_filenames, inputs_base_dir, outputs_zip, variables):
   with build_utils.TempDir() as temp_dir:
     for input_filename in input_filenames:
       relpath = os.path.relpath(os.path.abspath(input_filename),
@@ -41,7 +54,7 @@
       output_filename = os.path.join(temp_dir, relpath)
       parent_dir = os.path.dirname(output_filename)
       build_utils.MakeDirectory(parent_dir)
-      ProcessFile(input_filename, output_filename, variables)
+      ProcessFile(env, input_filename, output_filename, variables)
 
     build_utils.ZipDir(outputs_zip, temp_dir)
 
@@ -82,14 +95,17 @@
     name, _, value = v.partition('=')
     variables[name] = value
 
+  loader = RecordingFileSystemLoader(build_utils.CHROMIUM_SRC)
+  env = jinja2.Environment(loader=loader, undefined=jinja2.StrictUndefined,
+                           line_comment_prefix='##')
   if options.output:
-    ProcessFile(inputs[0], options.output, variables)
+    ProcessFile(env, inputs[0], options.output, variables)
   else:
-    ProcessFiles(inputs, options.inputs_base_dir, options.outputs_zip,
+    ProcessFiles(env, inputs, options.inputs_base_dir, options.outputs_zip,
                  variables)
 
   if options.depfile:
-    deps = inputs + build_utils.GetPythonDependencies()
+    deps = loader.get_loaded_templates() + build_utils.GetPythonDependencies()
     build_utils.WriteDepfile(options.depfile, deps)
 
 
diff --git a/build/android/jinja_template.gypi b/build/android/jinja_template.gypi
index 25430ca..9c49360 100644
--- a/build/android/jinja_template.gypi
+++ b/build/android/jinja_template.gypi
@@ -45,6 +45,7 @@
         'jinja_output%': '',
         'jinja_outputs_zip%': '',
         'jinja_inputs_base_dir%': '',
+        'jinja_includes%': [],
         'jinja_variables%': [],
         'jinja_args': [],
       },
@@ -52,6 +53,7 @@
         '<(DEPTH)/build/android/gyp/util/build_utils.py',
         '<(DEPTH)/build/android/gyp/jinja_template.py',
         '<@(jinja_inputs)',
+        '<@(jinja_includes)',
       ],
       'conditions': [
         ['jinja_output != ""', {
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index ba476d5..b16de84 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -70,37 +70,8 @@
     <ignore path="org/chromium/base/AnimationFrameTimeHistogram$Recorder.class"/>
     <ignore path="org/chromium/base/JavaHandlerThread.class"/>
     <ignore path="org/chromium/base/SysUtils.class"/>
-    <ignore path="org/chromium/chrome/browser/preferences/website/AddExceptionPreference.class"/>
-    <ignore path="org/chromium/chrome/browser/infobar/AnimationHelper$*.class"/>
-    <ignore path="org/chromium/chrome/browser/infobar/AppBannerInfoBar.class"/>
-    <ignore path="org/chromium/chrome/browser/BookmarkUtils.class"/>
-    <ignore path="org/chromium/chrome/browser/widget/ButtonCompat.class"/>
-    <ignore path="org/chromium/chrome/browser/autofill/CardUnmaskPrompt.class"/>
-    <ignore path="org/chromium/chrome/browser/LollipopTtsPlatformImpl.class"/>
-    <ignore path="org/chromium/chrome/browser/LollipopTtsPlatformImpl$*.class"/>
     <ignore path="org/chromium/chrome/browser/TtsPlatformImpl.class"/>
     <ignore path="org/chromium/chrome/browser/TtsPlatformImpl$*.class"/>
-    <ignore path="org/chromium/content/browser/accessibility/BrowserAccessibilityManager.class"/>
-    <ignore path="org/chromium/content/browser/ContentViewCore.class"/>
-    <ignore path="org/chromium/content/browser/accessibility/JellyBeanAccessibilityInjector.class"/>
-    <ignore path="org/chromium/content/browser/accessibility/JellyBeanBrowserAccessibilityManager$*.class"/>
-    <ignore path="org/chromium/content/browser/accessibility/LollipopAccessibilityInjector.class"/>
-    <ignore path="org/chromium/content/browser/accessibility/LollipopAccessibilityInjector$*.class"/>
-    <ignore path="org/chromium/content/browser/accessibility/LollipopBrowserAccessibilityManager.class"/>
-    <ignore path="org/chromium/media/AudioManagerAndroid.class"/>
-    <ignore path="org/chromium/media/MediaCodecBridge.class"/>
-    <ignore path="org/chromium/media/MediaDrmBridge.class"/>
-    <ignore path="org/chromium/media/MediaDrmBridge$*.class"/>
-    <ignore path="org/chromium/media/VideoCaptureCamera.class"/>
-    <ignore path="org/chromium/media/VideoCaptureCamera2.class"/>
-    <ignore path="org/chromium/media/VideoCaptureCamera2$*.class"/>
-    <ignore path="org/chromium/media/WebAudioMediaCodecBridge.class"/>
-    <ignore path="org/chromium/ui/base/Clipboard.class"/>
-    <ignore path="org/chromium/ui/ColorPickerAdvancedComponent.class"/>
-    <ignore path="org/chromium/ui/gfx/DeviceDisplayInfo.class"/>
-    <ignore path="org/chromium/ui/gl/SurfaceTexturePlatformWrapper.class"/>
-    <ignore path="org/chromium/ui/widget/TextViewWithClickableSpans.class"/>
-    <ignore path="org/chromium/ui/picker/TwoFieldDatePicker.class"/>
   </issue>
   <issue id="OldTargetApi">
     <ignore path="AndroidManifest.xml"/>
diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py
index c954508..36f8f48 100644
--- a/build/android/pylib/device/adb_wrapper.py
+++ b/build/android/pylib/device/adb_wrapper.py
@@ -300,7 +300,8 @@
           device_serial=self._device_serial)
 
   def Logcat(self, clear=False, dump=False, filter_specs=None,
-             logcat_format=None, timeout=None, retries=_DEFAULT_RETRIES):
+             logcat_format=None, ring_buffer=None, timeout=None,
+             retries=_DEFAULT_RETRIES):
     """Get an iterable over the logcat output.
 
     Args:
@@ -310,6 +311,9 @@
       logcat_format: If set, the format in which the logcat should be output.
         Options include "brief", "process", "tag", "thread", "raw", "time",
         "threadtime", and "long"
+      ring_buffer: If set, a list of alternate ring buffers to request.
+        Options include "main", "system", "radio", "events", "crash" or "all".
+        The default is equivalent to ["main", "system", "crash"].
       timeout: (optional) If set, timeout per try in seconds. If clear or dump
         is set, defaults to _DEFAULT_TIMEOUT.
       retries: (optional) If clear or dump is set, the number of retries to
@@ -328,6 +332,9 @@
       use_iter = False
     if logcat_format:
       cmd.extend(['-v', logcat_format])
+    if ring_buffer:
+      for buffer_name in ring_buffer:
+        cmd.extend(['-b', buffer_name])
     if filter_specs:
       cmd.extend(filter_specs)
 
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index 292752a..9f538e1 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -9,6 +9,7 @@
 # pylint: disable=unused-argument
 
 import collections
+import contextlib
 import itertools
 import logging
 import multiprocessing
@@ -71,7 +72,6 @@
   },
 ]
 
-
 @decorators.WithExplicitTimeoutAndRetries(
     _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
 def GetAVDs():
@@ -1453,8 +1453,16 @@
     # Skip the first line, which is just a header.
     for line in self.RunShellCommand(
         ['dumpsys', 'battery'], check_return=True)[1:]:
-      k, v = line.split(': ', 1)
-      result[k.strip()] = v.strip()
+      # If usb charging has been disabled, an extra line of header exists.
+      if 'UPDATES STOPPED' in line:
+        logging.warning('Dumpsys battery not receiving updates. '
+                        'Run dumpsys battery reset if this is in error.')
+      elif ':' not in line:
+        logging.warning('Unknown line found in dumpsys battery.')
+        logging.warning(line)
+      else:
+        k, v = line.split(': ', 1)
+        result[k.strip()] = v.strip()
     return result
 
   @decorators.WithTimeoutAndRetriesFromInstance()
@@ -1504,6 +1512,81 @@
 
     timeout_retry.WaitFor(set_and_verify_charging, wait_period=1)
 
+  # TODO(rnephew): Make private when all use cases can use the context manager.
+  @decorators.WithTimeoutAndRetriesFromInstance()
+  def DisableBatteryUpdates(self, timeout=None, retries=None):
+    """ Resets battery data and makes device appear like it is not
+    charging so that it will collect power data since last charge.
+
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+    """
+    def battery_updates_disabled():
+      return self.GetCharging() is False
+
+    self.RunShellCommand(
+        ['dumpsys', 'batterystats', '--reset'], check_return=True)
+    battery_data = self.RunShellCommand(
+        ['dumpsys', 'batterystats', '--charged', '--checkin'],
+        check_return=True)
+    ROW_TYPE_INDEX = 3
+    PWI_POWER_INDEX = 5
+    for line in battery_data:
+      l = line.split(',')
+      if (len(l) > PWI_POWER_INDEX and l[ROW_TYPE_INDEX] == 'pwi'
+          and l[PWI_POWER_INDEX] != 0):
+        raise device_errors.CommandFailedError(
+            'Non-zero pmi value found after reset.')
+    self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'],
+                         check_return=True)
+    timeout_retry.WaitFor(battery_updates_disabled, wait_period=1)
+
+  # TODO(rnephew): Make private when all use cases can use the context manager.
+  @decorators.WithTimeoutAndRetriesFromInstance()
+  def EnableBatteryUpdates(self, timeout=None, retries=None):
+    """ Restarts device charging so that dumpsys no longer collects power data.
+
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+    """
+    def battery_updates_enabled():
+      return self.GetCharging() is True
+
+    self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'],
+                         check_return=True)
+    self.RunShellCommand(['dumpsys', 'battery', 'reset'], check_return=True)
+    timeout_retry.WaitFor(battery_updates_enabled, wait_period=1)
+
+  @contextlib.contextmanager
+  def BatteryMeasurement(self, timeout=None, retries=None):
+    """Context manager that enables battery data collection. It makes
+    the device appear to stop charging so that dumpsys will start collecting
+    power data since last charge. Once the with block is exited, charging is
+    resumed and power data since last charge is no longer collected.
+
+    Only for devices L and higher.
+
+    Example usage:
+      with BatteryMeasurement():
+        browser_actions()
+        get_power_data() # report usage within this block
+      after_measurements() # Anything that runs after power
+                           # measurements are collected
+
+    Args:
+      timeout: timeout in seconds
+      retries: number of retries
+    """
+    if self.build_version_sdk < constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP:
+      raise device_errors.CommandFailedError('Device must be L or higher.')
+    try:
+      self.DisableBatteryUpdates(timeout=timeout, retries=retries)
+      yield
+    finally:
+      self.EnableBatteryUpdates(timeout=timeout, retries=retries)
+
   @decorators.WithTimeoutAndRetriesFromInstance()
   def GetDevicePieWrapper(self, timeout=None, retries=None):
     """Gets the absolute path to the run_pie wrapper on the device.
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index 7c20f0d..98e3539 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -1537,6 +1537,29 @@
       self.device.SetCharging(False)
 
 
+class DeviceUtilsSetBatteryMeasurementTest(DeviceUtilsTest):
+
+  def testBatteryMeasurement(self):
+    with self.assertCalls(
+        (self.call.device.RunShellCommand(
+            mock.ANY, retries=0, single_line=True,
+            timeout=10, check_return=True), '22'),
+        (self.call.device.RunShellCommand(
+            ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
+        (self.call.device.RunShellCommand(
+            ['dumpsys', 'batterystats', '--charged', '--checkin'],
+            check_return=True), []),
+        (self.call.device.RunShellCommand(
+            ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
+        (self.call.device.GetCharging(), False),
+        (self.call.device.RunShellCommand(
+            ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True), []),
+        (self.call.device.RunShellCommand(
+            ['dumpsys', 'battery', 'reset'], check_return=True), []),
+        (self.call.device.GetCharging(), True)):
+      with self.device.BatteryMeasurement():
+        pass
+
 
 class DeviceUtilsStrTest(DeviceUtilsTest):
 
diff --git a/build/android/pylib/device/logcat_monitor.py b/build/android/pylib/device/logcat_monitor.py
index 1d4cc24..2eebc2d 100644
--- a/build/android/pylib/device/logcat_monitor.py
+++ b/build/android/pylib/device/logcat_monitor.py
@@ -19,8 +19,9 @@
 
 class LogcatMonitor(object):
 
-  # Format: <DATE> <TIME> <PID> <TID> <LEVEL> <COMPONENT>: <MESSAGE>
-  _THREADTIME_RE_FORMAT = r'\S* +\S* +(%s) +(%s) +(%s) +(%s): +(%s)$'
+  _THREADTIME_RE_FORMAT = (
+      r'(?P<date>\S*) +(?P<time>\S*) +(?P<proc_id>%s) +(?P<thread_id>%s) +'
+      r'(?P<log_level>%s) +(?P<component>%s) *: +(?P<message>%s)$')
 
   def __init__(self, adb, clear=True, filter_specs=None):
     """Create a LogcatMonitor instance.
@@ -97,18 +98,12 @@
       log_level: The log level to match. If None, matches any log level.
       component: The component to match. If None, matches any component.
 
-    Returns:
-      An iterable containing objects with five attributes:
-        |proc_id|: the process ID
-        |thread_id|: the thread ID
-        |log_level|: the log level
-        |component|: the component
-        |message|: the logcat message
+    Yields:
+      A match object for each matching line in the logcat. The match object
+      will always contain, in addition to groups defined in |message_regex|,
+      the following named groups: 'date', 'time', 'proc_id', 'thread_id',
+      'log_level', 'component', and 'message'.
     """
-    LogcatLine = collections.namedtuple(
-        'LogcatLine',
-        ['proc_id', 'thread_id', 'log_level', 'component', 'message'])
-
     if proc_id is None:
       proc_id = r'\d+'
     if thread_id is None:
@@ -121,11 +116,10 @@
         type(self)._THREADTIME_RE_FORMAT % (
             proc_id, thread_id, log_level, component, message_regex))
 
-    regexed_lines = (
-        re.match(threadtime_re, l)
-        for l in self._adb.Logcat(dump=True, logcat_format='threadtime'))
-    only_matches = (m for m in regexed_lines if m)
-    return (LogcatLine(*m.group(1, 2, 3, 4, 5)) for m in only_matches)
+    for line in self._adb.Logcat(dump=True, logcat_format='threadtime'):
+      m = re.match(threadtime_re, line)
+      if m:
+        yield m
 
   def Start(self):
     """Starts the logcat monitor.
diff --git a/build/android/pylib/device/logcat_monitor_test.py b/build/android/pylib/device/logcat_monitor_test.py
index 7a6bf56..db397e5 100755
--- a/build/android/pylib/device/logcat_monitor_test.py
+++ b/build/android/pylib/device/logcat_monitor_test.py
@@ -50,11 +50,11 @@
       self.assertIsNotNone(
           actual,
           msg='actual is missing elements starting with %s' % str(expected))
-      self.assertEqual(actual.proc_id, expected[0])
-      self.assertEqual(actual.thread_id, expected[1])
-      self.assertEqual(actual.log_level, expected[2])
-      self.assertEqual(actual.component, expected[3])
-      self.assertEqual(actual.message, expected[4])
+      self.assertEqual(actual.group('proc_id'), expected[0])
+      self.assertEqual(actual.group('thread_id'), expected[1])
+      self.assertEqual(actual.group('log_level'), expected[2])
+      self.assertEqual(actual.group('component'), expected[3])
+      self.assertEqual(actual.group('message'), expected[4])
 
     with self.assertRaises(StopIteration):
       next(actual_iter)
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py
index 3859669..eb83d68 100644
--- a/build/android/pylib/forwarder.py
+++ b/build/android/pylib/forwarder.py
@@ -51,18 +51,12 @@
   _DEVICE_FORWARDER_PATH = (constants.TEST_EXECUTABLE_DIR +
                             '/forwarder/device_forwarder')
   _LOCK_PATH = '/tmp/chrome.forwarder.lock'
-  _MULTIPROCESSING_ENV_VAR = 'CHROME_FORWARDER_USE_MULTIPROCESSING'
   # Defined in host_forwarder_main.cc
   _HOST_FORWARDER_LOG = '/tmp/host_forwarder_log'
 
   _instance = None
 
   @staticmethod
-  def UseMultiprocessing():
-    """Tells the forwarder that multiprocessing is used."""
-    os.environ[Forwarder._MULTIPROCESSING_ENV_VAR] = '1'
-
-  @staticmethod
   def Map(port_pairs, device, tool=None):
     """Runs the forwarder.
 
@@ -245,13 +239,10 @@
   def _GetPidForLock():
     """Returns the PID used for host_forwarder initialization.
 
-    In case multi-process sharding is used, the PID of the "sharder" is used.
-    The "sharder" is the initial process that forks that is the parent process.
-    By default, multi-processing is not used. In that case the PID of the
-    current process is returned.
+    The PID of the "sharder" is used to handle multiprocessing. The "sharder"
+    is the initial process that forks that is the parent process.
     """
-    use_multiprocessing = Forwarder._MULTIPROCESSING_ENV_VAR in os.environ
-    return os.getpgrp() if use_multiprocessing else os.getpid()
+    return os.getpgrp()
 
   def _InitHostLocked(self):
     """Initializes the host forwarder daemon.
diff --git a/build/android/pylib/junit/test_runner.py b/build/android/pylib/junit/test_runner.py
index 35ac666..b85967b 100644
--- a/build/android/pylib/junit/test_runner.py
+++ b/build/android/pylib/junit/test_runner.py
@@ -22,9 +22,13 @@
 
   def RunTest(self, _test):
     """Runs junit tests from |self._test_suite|."""
+
     command = ['java',
+               '-Drobolectric.dependency.dir=%s' %
+                    os.path.join(constants.GetOutDirectory(), 'lib.java'),
                '-jar', os.path.join(constants.GetOutDirectory(), 'lib.java',
                                     '%s.jar' % self._test_suite)]
+
     if self._test_filter:
       command.extend(['-gtest-filter', self._test_filter])
     if self._package_filter:
diff --git a/build/android/pylib/perf/setup.py b/build/android/pylib/perf/setup.py
index 99c3e19..8884d60 100644
--- a/build/android/pylib/perf/setup.py
+++ b/build/android/pylib/perf/setup.py
@@ -72,7 +72,6 @@
 
   # Before running the tests, kill any leftover server.
   test_environment.CleanupLeftoverProcesses()
-  forwarder.Forwarder.UseMultiprocessing()
 
   # We want to keep device affinity, so return all devices ever seen.
   all_devices = _GetAllDevices()
diff --git a/build/android/pylib/remote/device/remote_device_gtest_run.py b/build/android/pylib/remote/device/remote_device_gtest_run.py
index 76d1d45..973eebe 100644
--- a/build/android/pylib/remote/device/remote_device_gtest_run.py
+++ b/build/android/pylib/remote/device/remote_device_gtest_run.py
@@ -73,20 +73,15 @@
   def _ParseTestResults(self):
     logging.info('Parsing results from stdout.')
     results = base_test_result.TestRunResults()
-    if self._results['results']['exception']:
+    output = self._results['results']['output'].splitlines()
+    output = (l[len(self._INSTRUMENTATION_STREAM_LEADER):] for l in output
+              if l.startswith(self._INSTRUMENTATION_STREAM_LEADER))
+    results_list = self._test_instance.ParseGTestOutput(output)
+    results.AddResults(results_list)
+    if self._env.only_output_failures:
+      logging.info('See logcat for more results information.')
+    if not self._results['results']['pass']:
       results.AddResult(base_test_result.BaseTestResult(
-          self._results['results']['exception'],
+          'Remote Service detected error.',
           base_test_result.ResultType.FAIL))
-    else:
-      output = self._results['results']['output'].splitlines()
-      output = (l[len(self._INSTRUMENTATION_STREAM_LEADER):] for l in output
-                if l.startswith(self._INSTRUMENTATION_STREAM_LEADER))
-      results_list = self._test_instance.ParseGTestOutput(output)
-      results.AddResults(results_list)
-      if self._env.only_output_failures:
-        logging.info('See logcat for more results information.')
-      if not self._results['results']['pass']:
-        results.AddResult(base_test_result.BaseTestResult(
-            'Remote Service detected error.',
-            base_test_result.ResultType.FAIL))
-    return results
+    return results
\ No newline at end of file
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 709a30c..fe173a4 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
@@ -33,13 +33,6 @@
   def _ParseTestResults(self):
     logging.info('Parsing results from stdout.')
     r = base_test_result.TestRunResults()
-
-    if self._results['results']['exception']:
-      r.AddResult(base_test_result.BaseTestResult(
-          self._results['results']['exception'],
-          base_test_result.ResultType.FAIL))
-      return r
-
     result_code, result_bundle, statuses = (
         self._test_instance.ParseAmInstrumentRawOutput(
             self._results['results']['output'].splitlines()))
diff --git a/build/android/pylib/remote/device/remote_device_test_run.py b/build/android/pylib/remote/device/remote_device_test_run.py
index a2d4a53..4a155ac 100644
--- a/build/android/pylib/remote/device/remote_device_test_run.py
+++ b/build/android/pylib/remote/device/remote_device_test_run.py
@@ -99,6 +99,11 @@
         timeout_counter += self.WAIT_TIME
         heartbeat_counter += self.WAIT_TIME
       self._DownloadTestResults(self._env.results_path)
+
+      if self._results['results']['exception']:
+        raise remote_device_helper.RemoteDeviceError(
+            self._results['results']['exception'], is_infra_error=True)
+
       return self._ParseTestResults()
 
   #override
diff --git a/build/android/tombstones.py b/build/android/tombstones.py
index 9b8a8b2..1a958ac 100755
--- a/build/android/tombstones.py
+++ b/build/android/tombstones.py
@@ -10,6 +10,8 @@
 # Assumes tombstone file was created with current symbols.
 
 import datetime
+import itertools
+import logging
 import multiprocessing
 import os
 import re
@@ -20,6 +22,7 @@
 from pylib import android_commands
 from pylib.device import device_errors
 from pylib.device import device_utils
+from pylib.utils import run_tests_helper
 
 
 _TZ_UTC = {'TZ': 'UTC'}
@@ -33,15 +36,18 @@
   Yields:
     Tuples of (tombstone filename, date time of file on device).
   """
-  lines = device.RunShellCommand(
-      ['ls', '-a', '-l', '/data/tombstones'],
-      as_root=True, check_return=True, env=_TZ_UTC, timeout=60)
-  for line in lines:
-    if 'tombstone' in line and not 'No such file or directory' in line:
-      details = line.split()
-      t = datetime.datetime.strptime(details[-3] + ' ' + details[-2],
-                                     '%Y-%m-%d %H:%M')
-      yield details[-1], t
+  try:
+    lines = device.RunShellCommand(
+        ['ls', '-a', '-l', '/data/tombstones'],
+        as_root=True, check_return=True, env=_TZ_UTC, timeout=60)
+    for line in lines:
+      if 'tombstone' in line and not 'No such file or directory' in line:
+        details = line.split()
+        t = datetime.datetime.strptime(details[-3] + ' ' + details[-2],
+                                       '%Y-%m-%d %H:%M')
+        yield details[-1], t
+  except device_errors.CommandFailedError:
+    logging.exception('Could not retrieve tombstones.')
 
 
 def _GetDeviceDateTime(device):
@@ -133,8 +139,8 @@
             ', about this long ago: ' +
             (str(tombstone['device_now'] - tombstone['time']) +
             ' Device: ' + tombstone['serial'])]
-  print '\n'.join(lines)
-  print 'Resolving...'
+  logging.info('\n'.join(lines))
+  logging.info('Resolving...')
   lines += _ResolveSymbols(tombstone['data'], tombstone['stack'],
                            tombstone['device_abi'])
   return lines
@@ -148,15 +154,15 @@
     tombstones: a list of tombstones.
   """
   if not tombstones:
-    print 'No device attached?  Or no tombstones?'
+    logging.warning('No tombstones to resolve.')
     return
   if len(tombstones) == 1:
     data = _ResolveTombstone(tombstones[0])
   else:
     pool = multiprocessing.Pool(processes=jobs)
     data = pool.map(_ResolveTombstone, tombstones)
-    data = ['\n'.join(d) for d in data]
-  print '\n'.join(data)
+  for d in data:
+    logging.info(d)
 
 
 def _GetTombstonesForDevice(device, options):
@@ -169,7 +175,7 @@
   ret = []
   all_tombstones = list(_ListTombstones(device))
   if not all_tombstones:
-    print 'No device attached?  Or no tombstones?'
+    logging.warning('No tombstones.')
     return ret
 
   # Sort the tombstones in date order, descending
@@ -192,7 +198,7 @@
     for line in device.RunShellCommand(
         ['ls', '-a', '-l', '/data/tombstones'],
         as_root=True, check_return=True, env=_TZ_UTC, timeout=60):
-      print '%s: %s' % (str(device), line)
+      logging.info('%s: %s', str(device), line)
     raise
 
   # Erase all the tombstones if desired.
@@ -204,6 +210,11 @@
 
 
 def main():
+  custom_handler = logging.StreamHandler(sys.stdout)
+  custom_handler.setFormatter(run_tests_helper.CustomFormatter())
+  logging.getLogger().addHandler(custom_handler)
+  logging.getLogger().setLevel(logging.INFO)
+
   parser = optparse.OptionParser()
   parser.add_option('--device',
                     help='The serial number of the device. If not specified '
@@ -226,6 +237,9 @@
   else:
     devices = android_commands.GetAttachedDevices()
 
+  # This must be done serially because strptime can hit a race condition if
+  # used for the first time in a multithreaded environment.
+  # http://bugs.python.org/issue7980
   tombstones = []
   for device_serial in devices:
     device = device_utils.DeviceUtils(device_serial)
diff --git a/build/common.gypi b/build/common.gypi
index 61224ba..e900cc4 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -197,7 +197,7 @@
           }],
 
           # Set default value of toolkit_views based on OS.
-          ['OS=="win" or chromeos==1 or use_aura==1', {
+          ['OS=="mac" or OS=="win" or chromeos==1 or use_aura==1', {
             'toolkit_views%': 1,
           }, {
             'toolkit_views%': 0,
@@ -210,8 +210,8 @@
             'toolkit_views%': 0,
           }],
 
-          # Enable HiDPI on Mac OS, Chrome OS and Windows.
-          ['OS=="mac" or chromeos==1 or OS=="win"', {
+          # Enable HiDPI on Mac OS, Chrome OS, Windows and Linux.
+          ['OS=="mac" or chromeos==1 or OS=="win" or OS=="linux"', {
             'enable_hidpi%': 1,
           }],
 
@@ -438,7 +438,7 @@
       # Track where uninitialized memory originates from. From fastest to
       # slowest: 0 - no tracking, 1 - track only the initial allocation site, 2
       # - track the chain of stores leading from allocation site to use site.
-      'msan_track_origins%': 1,
+      'msan_track_origins%': 2,
 
       # Enable building with UBSan (Clang's -fsanitize=undefined option).
       # -fsanitize=undefined only works with clang, but ubsan=1 implies clang=1
@@ -450,10 +450,16 @@
       # -fsanitize=vptr only works with clang, but ubsan_vptr=1 implies clang=1
       'ubsan_vptr%': 0,
 
-      # Use the dynamic libraries instrumented by one of the sanitizers
-      # instead of the standard system libraries.
+      # Use dynamic libraries instrumented by one of the sanitizers
+      # instead of the standard system libraries. Set this flag to build the
+      # libraries from source.
       'use_instrumented_libraries%': 0,
 
+      # Use dynamic libraries instrumented by one of the sanitizers
+      # instead of the standard system libraries. Set this flag to download
+      # prebuilt binaries from GCS.
+      'use_prebuilt_instrumented_libraries%': 0,
+
       # Use libc++ (third_party/libc++ and third_party/libc++abi) instead of
       # stdlibc++ as standard library. This is intended to use for instrumented
       # builds.
@@ -647,6 +653,9 @@
       # See http://clang.llvm.org/docs/ControlFlowIntegrity.html
       'cfi_vptr%': 0,
 
+      # Whether the entire browser uses toolkit-views on Mac instead of Cocoa.
+      'mac_views_browser%': 0,
+
       'conditions': [
         # A flag for POSIX platforms
         ['OS=="win"', {
@@ -1150,6 +1159,7 @@
     'ubsan_blacklist%': '<(ubsan_blacklist)',
     'ubsan_vptr%': '<(ubsan_vptr)',
     'use_instrumented_libraries%': '<(use_instrumented_libraries)',
+    'use_prebuilt_instrumented_libraries%': '<(use_prebuilt_instrumented_libraries)',
     'use_custom_libcxx%': '<(use_custom_libcxx)',
     'use_system_libcxx%': '<(use_system_libcxx)',
     'clang_type_profiler%': '<(clang_type_profiler)',
@@ -1214,6 +1224,7 @@
     'video_hole%': '<(video_hole)',
     'v8_use_external_startup_data%': '<(v8_use_external_startup_data)',
     'cfi_vptr%': '<(cfi_vptr)',
+    'mac_views_browser%': '<(mac_views_browser)',
 
     # Use system protobuf instead of bundled one.
     'use_system_protobuf%': 0,
@@ -1469,10 +1480,6 @@
     # Compile d8 for the host toolset.
     'v8_toolset_for_d8': 'host',
 
-    # Enable the V8 heap verification code. The verification itself is enabled
-    # via a command line option.
-    'v8_enable_verify_heap%': 1,
-
     # Use brlapi from brltty for braille display support.
     'use_brlapi%': 0,
 
@@ -1503,9 +1510,6 @@
     'ozone_platform_ozonex%': 0,
     'ozone_platform_test%': 0,
 
-    # Whether the browser is non-native (using Views Toolkit) on Mac.
-    'mac_views_browser%': 0,
-
     # Experiment: http://crbug.com/426914
     'envoy%': 0,
 
@@ -1951,9 +1955,7 @@
             'win_use_allocator_shim%': 0,
           }],
           ['syzyasan==1', {
-            # Uncomment the following line to enable Kasko for
-            # SyzyASAN-instrumented releases.
-            # 'kasko%': 1,
+            'kasko%': 1,
           }],
           ['component=="shared_library" and "<(GENERATOR)"=="ninja"', {
             # Only enabled by default for ninja because it's buggy in VS.
@@ -2078,6 +2080,11 @@
           '-E', 'ANDROID_JAVA_TAGGED_ONLY=true',
           '--no-output-all-resource-defines',
         ],
+        'conditions': [
+          ['<(android_webview_build)==1', {
+            'grit_defines': ['-D', 'is_android_webview_build'],
+          }],
+        ],
       }],
       ['OS=="mac" or OS=="ios"', {
         'grit_defines': ['-D', 'scale_factors=2x'],
@@ -2155,6 +2162,9 @@
       ['enable_wifi_bootstrapping==1', {
         'grit_defines': ['-D', 'enable_wifi_bootstrapping'],
       }],
+      ['mac_views_browser==1', {
+        'grit_defines': ['-D', 'mac_views_browser'],
+      }],
       ['enable_resource_whitelist_generation==1 and OS!="win"', {
         'grit_rc_header_format': ['-h', '#define {textual_id} _Pragma("whitelisted_resource_{numeric_id}") {numeric_id}'],
       }],
@@ -2451,7 +2461,7 @@
       'chromecast%': '<(chromecast)',
 
       # See http://msdn.microsoft.com/en-us/library/aa652360(VS.71).aspx
-      'win_release_Optimization%': '2', # 2 = /Os
+      'win_release_Optimization%': '2', # 2 = /O2
       'win_debug_Optimization%': '0',   # 0 = /Od
 
       # See http://msdn.microsoft.com/en-us/library/2kxx5t2c(v=vs.80).aspx
@@ -3002,12 +3012,6 @@
       ['v8_use_external_startup_data==1', {
        'defines': ['V8_USE_EXTERNAL_STARTUP_DATA'],
       }],
-      ['use_lto==1 and (target_arch=="ia32" or target_arch=="x64")', {
-        # Required for third_party/zlib/crc_folding.c and various other code
-        # that uses SSE. TODO(pcc): Remove this once we properly support
-        # subtarget specific code generation in LLVM.
-        'ldflags': ['-Wl,-plugin-opt,mcpu=corei7-avx'],
-      }],
     ],  # conditions for 'target_defaults'
     'target_conditions': [
       ['<(use_libpci)==1', {
@@ -3083,7 +3087,14 @@
               '_CRT_NONSTDC_NO_DEPRECATE',
               '_SCL_SECURE_NO_DEPRECATE',
             ],
-            'msvs_disabled_warnings': [4800],
+            'msvs_disabled_warnings': [
+              # These are variable shadowing warnings that are new in VS2015.
+              # We should probably work through these at some point for
+              # non-chromium code, but for now, focus on chromium_code==1 code.
+              4456, 4457, 4458, 4459,
+
+              4800,
+            ],
             'msvs_settings': {
               'VCCLCompilerTool': {
                 'WarningLevel': '3',
@@ -3097,11 +3108,6 @@
                   'VCCLCompilerTool': { 'WarnAsError': 'false' },
                 }
               }],
-              ['clang==1', {
-                'msvs_settings': {
-                  'VCCLCompilerTool': { 'WarnAsError': 'false' },
-                }
-              }],
               [ 'component=="shared_library"', {
               # TODO(darin): Unfortunately, some third_party code depends on base.
                 'msvs_disabled_warnings': [
@@ -3205,7 +3211,8 @@
               # Suggested by Microsoft Devrel to avoid
               #   LINK : fatal error LNK1248: image size (80000000) exceeds maximum allowable size (80000000)
               # which started happening more regularly after VS2013 Update 4.
-              '/maxilksize:2147483647',
+              # Needs to be a bit lower for VS2015, or else errors out.
+              '/maxilksize:0x7ff00000',
             ],
           },
         },
@@ -4413,6 +4420,11 @@
               '<(DEPTH)/third_party/instrumented_libraries/instrumented_libraries.gyp:instrumented_libraries',
             ],
           }],
+          ['use_prebuilt_instrumented_libraries==1', {
+            'dependencies': [
+              '<(DEPTH)/third_party/instrumented_libraries/instrumented_libraries.gyp:prebuilt_instrumented_libraries',
+            ],
+          }],
           ['use_custom_libcxx==1', {
             'dependencies': [
               '<(DEPTH)/buildtools/third_party/libc++/libc++.gyp:libcxx_proxy',
@@ -5475,6 +5487,11 @@
         ],
         'msvs_cygwin_shell': 0,
         'msvs_disabled_warnings': [
+          # C4091: 'typedef ': ignored on left of 'X' when no variable is
+          #                    declared.
+          # This happens in a number of Windows headers. Dumb.
+          4091,
+
           # C4127: conditional expression is constant
           # This warning can in theory catch dead code and other problems, but
           # triggers in far too many desirable cases where the conditional
@@ -5930,6 +5947,41 @@
         ],
       },
     }],
+    ['use_lto==1 and clang==1 and (target_arch=="ia32" or target_arch=="x64")', {
+      'target_defaults': {
+        'target_conditions': [
+          # Required for third_party/zlib/crc_folding.c and various other code
+          # that uses SSE. TODO(pcc): Remove this once we properly support
+          # subtarget specific code generation in LLVM.
+          ['_toolset=="target"', {
+            'ldflags': [
+              '-Wl,-plugin-opt,mcpu=corei7-avx',
+            ],
+          }],
+          ['_toolset=="target" and _type!="static_library"', {
+            'xcode_settings':  {
+              'OTHER_LDFLAGS': [
+                '-Wl,-mcpu,corei7-avx',
+              ],
+            },
+          }],
+        ],
+      },
+    }],
+    ['use_lto==1 and clang==1 and target_arch=="arm"', {
+      'target_defaults': {
+        'target_conditions': [
+          ['_toolset=="target"', {
+            # Without this flag, LTO produces a .text section that is larger
+            # than the maximum call displacement, preventing the linker from
+            # relocating calls (http://llvm.org/PR22999).
+            'ldflags': [
+              '-Wl,-plugin-opt,-function-sections',
+            ],
+          }],
+        ],
+      },
+    }],
     ['(use_lto==1 or use_lto_o2==1) and clang==0', {
       'target_defaults': {
         'target_conditions': [
@@ -5962,6 +6014,18 @@
             'ldflags': [
               '-fsanitize=cfi-vptr',
             ],
+            'xcode_settings': {
+              'OTHER_CFLAGS': [
+                '-fsanitize=cfi-vptr',
+              ],
+            },
+          }],
+          ['_toolset=="target" and _type!="static_library"', {
+            'xcode_settings':  {
+              'OTHER_LDFLAGS': [
+                '-fsanitize=cfi-vptr',
+              ],
+            },
           }],
         ],
       },
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 3afba89..8e30c4a 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -416,15 +416,8 @@
 
 # Windows linker setup for EXEs and DLLs.
 if (is_win) {
-  if (is_debug) {
-    _default_incremental_linking_config =
-        "//build/config/win:incremental_linking"
-  } else {
-    _default_incremental_linking_config =
-        "//build/config/win:no_incremental_linking"
-  }
   _windows_linker_configs = [
-    _default_incremental_linking_config,
+    "//build/config/win:default_incremental_linking",
     "//build/config/win:sdk_link",
     "//build/config/win:common_linker_setup",
 
diff --git a/build/config/ui.gni b/build/config/ui.gni
index 46154a3..787e7ef 100644
--- a/build/config/ui.gni
+++ b/build/config/ui.gni
@@ -28,7 +28,10 @@
   use_aura = is_win || is_linux
 
   # True means the UI is built using the "views" framework.
-  toolkit_views = is_win || is_chromeos || use_aura
+  toolkit_views = is_mac || is_win || is_chromeos || use_aura
+
+  # Whether the entire browser uses toolkit-views on Mac instead of Cocoa.
+  mac_views_browser = false
 
   # Whether we should use glib, a low level C utility library.
   use_glib = is_linux && !use_ozone
@@ -59,4 +62,4 @@
 
 use_clipboard_aurax11 = is_linux && use_aura && use_x11
 
-enable_hidpi = is_mac || is_chromeos || is_win
+enable_hidpi = is_mac || is_chromeos || is_win || is_linux
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 3e98c1a..4ca22f7 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -106,11 +106,42 @@
 
 # Incremental linking ----------------------------------------------------------
 
+incremental_linking_on_switch = [ "/INCREMENTAL" ]
+incremental_linking_off_switch = [ "/INCREMENTAL:NO" ]
+
+# Applies incremental linking or not depending on the current configuration.
+config("default_incremental_linking") {
+  if (is_debug) {
+    ldflags = incremental_linking_on_switch
+  } else {
+    ldflags = incremental_linking_off_switch
+  }
+}
+
+# Explicitly on or off incremental linking
 config("incremental_linking") {
-  ldflags = [ "/INCREMENTAL" ]
+  ldflags = incremental_linking_on_switch
 }
 config("no_incremental_linking") {
-  ldflags = [ "/INCREMENTAL:NO" ]
+  ldflags = incremental_linking_off_switch
+}
+
+# Some large modules can't handle incremental linking in some situations. This
+# config should be applied to large modules to turn off incremental linking
+# when it won't work.
+config("default_large_module_incremental_linking") {
+  if (!is_debug) {
+    # Default is always off in release build.
+    ldflags = incremental_linking_off_switch
+  } else if ((symbol_level == 0 || symbol_level == 1) &&
+             (current_cpu == "x86" || !is_component_build)) {
+    # When full symbols are on, don't do incremental linking for large modules
+    # on 32-bit or in non-component mode as the toolchain fails due to the size
+    # of the .ilk files.
+    ldflags = incremental_linking_off_switch
+  } else {
+    ldflags = incremental_linking_on_switch
+  }
 }
 
 # Character set ----------------------------------------------------------------
diff --git a/build/get_landmines.py b/build/get_landmines.py
index d7c98a5..8f5b878 100755
--- a/build/get_landmines.py
+++ b/build/get_landmines.py
@@ -24,6 +24,12 @@
   """
   ALL LANDMINES ARE EMITTED FROM HERE.
   """
+  # DO NOT add landmines as part of a regular CL. Landmines are a last-effort
+  # bandaid fix if a CL that got landed has a build dependency bug and all bots
+  # need to be cleaned up. If you're writing a new CL that causes build
+  # dependency problems, fix the dependency problems instead of adding a
+  # landmine.
+
   if (distributor() == 'goma' and platform() == 'win32' and
       builder() == 'ninja'):
     print 'Need to clobber winja goma due to backend cwd cache fix.'
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index 32dd744..53d9712 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -73,11 +73,11 @@
 fi
 
 lsb_release=$(lsb_release --codename --short)
-ubuntu_codenames="(precise|quantal|raring|saucy|trusty|utopic)"
+ubuntu_codenames="(precise|trusty|utopic)"
 if [ 0 -eq "${do_unsupported-0}" ] && [ 0 -eq "${do_quick_check-0}" ] ; then
   if [[ ! $lsb_release =~ $ubuntu_codenames ]]; then
-    echo "ERROR: Only Ubuntu 12.04 (precise) through 14.10 (utopic) are"\
-        "currently supported" >&2
+    echo "ERROR: Only Ubuntu 12.04 (precise), 14.04 (trusty) and " \
+        "14.10 (utopic) are currently supported" >&2
     exit 1
   fi
 
@@ -136,8 +136,16 @@
           libpixman-1-0-dbg libsqlite3-0-dbg libx11-6-dbg libxau6-dbg
           libxcb1-dbg libxcomposite1-dbg libxcursor1-dbg libxdamage1-dbg
           libxdmcp6-dbg libxext6-dbg libxfixes3-dbg libxi6-dbg libxinerama1-dbg
-          libxrandr2-dbg libxrender1-dbg libxtst6-dbg zlib1g-dbg
-          libstdc++6-4.6-dbg"
+          libxrandr2-dbg libxrender1-dbg libxtst6-dbg zlib1g-dbg"
+
+# Find the proper version of libstdc++6-4.x-dbg.
+if [ "x$lsb_release" = "xprecise" ]; then
+  dbg_list="${dbg_list} libstdc++6-4.6-dbg"
+elif [ "x$lsb_release" = "xtrusty" ]; then
+  dbg_list="${dbg_list} libstdc++6-4.8-dbg"
+else
+  dbg_list="${dbg_list} libstdc++6-4.9-dbg"
+fi
 
 # 32-bit libraries needed e.g. to compile V8 snapshot for Android or armhf
 lib32_list="linux-libc-dev:i386"
@@ -163,8 +171,7 @@
 # it depends on mesa, and only one version of mesa can exists on the system.
 # Hence we must match the same version or this entire script will fail.
 mesa_variant=""
-for variant in "-lts-quantal" "-lts-raring" "-lts-saucy" "-lts-trusty" \
-               "-lts-utopic"; do
+for variant in "-lts-trusty" "-lts-utopic"; do
   if $(dpkg-query -Wf'${Status}' libgl1-mesa-glx${variant} 2>/dev/null | \
        grep -q " ok installed"); then
     mesa_variant="${variant}"
@@ -349,7 +356,7 @@
 fi
 
 if test "$do_inst_lib32" = "1" || test "$do_inst_nacl" = "1"; then
-  if [[ ! $lsb_release =~ (precise|quantal|raring) ]]; then
+  if [[ ! $lsb_release =~ (precise) ]]; then
     sudo dpkg --add-architecture i386
   fi
 fi
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index 43db731..76d2e1e 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -68,10 +68,6 @@
 "race:thread_manager\n"
 "race:v8::Locker::Initialize\n"
 
-// http://crbug.com/223352
-"race:uprv_malloc_54\n"
-"race:uprv_realloc_54\n"
-
 // http://crbug.com/239359
 "race:media::TestInputCallback::OnData\n"
 
diff --git a/build/secondary/third_party/icu/BUILD.gn b/build/secondary/third_party/icu/BUILD.gn
deleted file mode 100644
index 53818c9..0000000
--- a/build/secondary/third_party/icu/BUILD.gn
+++ /dev/null
@@ -1,554 +0,0 @@
-# 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.
-
-import("//third_party/icu/config.gni")
-
-# Meta target that includes both icuuc and icui18n. Most targets want both.
-# You can depend on the individually if you need to.
-group("icu") {
-  deps = [
-    ":icui18n",
-    ":icuuc",
-  ]
-}
-
-# Shared config used by ICU and all dependents.
-config("icu_config") {
-  defines = [
-    "U_USING_ICU_NAMESPACE=0",
-    "U_ENABLE_DYLOAD=0",
-  ]
-
-  if (component_mode != "shared_library") {
-    defines += [ "U_STATIC_IMPLEMENTATION" ]
-  }
-
-  include_dirs = [
-    "source/common",
-    "source/i18n",
-  ]
-}
-
-# Config used only by ICU code.
-config("icu_code") {
-  cflags = []
-  if (is_win) {
-    # Disable some compiler warnings.
-    cflags += [
-      "/wd4005",  # Macro redefinition.
-      "/wd4068",  # Unknown pragmas.
-      "/wd4267",  # Conversion from size_t on 64-bits.
-      "/wd4996",  # Deprecated functions.
-    ]
-  } else if (is_linux) {
-    cflags += [
-      # Since ICU wants to internally use its own deprecated APIs, don't
-      # complain about it.
-      "-Wno-deprecated-declarations",
-      "-Wno-unused-function",
-    ]
-  }
-  if (is_clang) {
-    cflags += [
-      "-Wno-deprecated-declarations",
-      "-Wno-logical-op-parentheses",
-      "-Wno-tautological-compare",
-      "-Wno-switch",
-    ]
-  }
-}
-
-component("icui18n") {
-  sources = [
-    "source/i18n/alphaindex.cpp",
-    "source/i18n/anytrans.cpp",
-    "source/i18n/astro.cpp",
-    "source/i18n/basictz.cpp",
-    "source/i18n/bocsu.cpp",
-    "source/i18n/brktrans.cpp",
-    "source/i18n/buddhcal.cpp",
-    "source/i18n/calendar.cpp",
-    "source/i18n/casetrn.cpp",
-    "source/i18n/cecal.cpp",
-    "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",
-    "source/i18n/cpdtrans.cpp",
-    "source/i18n/csdetect.cpp",
-    "source/i18n/csmatch.cpp",
-    "source/i18n/csr2022.cpp",
-    "source/i18n/csrecog.cpp",
-    "source/i18n/csrmbcs.cpp",
-    "source/i18n/csrsbcs.cpp",
-    "source/i18n/csrucode.cpp",
-    "source/i18n/csrutf8.cpp",
-    "source/i18n/curramt.cpp",
-    "source/i18n/currfmt.cpp",
-    "source/i18n/currpinf.cpp",
-    "source/i18n/currunit.cpp",
-    "source/i18n/dangical.cpp",
-    "source/i18n/datefmt.cpp",
-    "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",
-    "source/i18n/dtfmtsym.cpp",
-    "source/i18n/dtitvfmt.cpp",
-    "source/i18n/dtitvinf.cpp",
-    "source/i18n/dtptngen.cpp",
-    "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",
-    "source/i18n/fphdlimp.cpp",
-    "source/i18n/fpositer.cpp",
-    "source/i18n/funcrepl.cpp",
-    "source/i18n/gender.cpp",
-    "source/i18n/gregocal.cpp",
-    "source/i18n/gregoimp.cpp",
-    "source/i18n/hebrwcal.cpp",
-    "source/i18n/identifier_info.cpp",
-    "source/i18n/indiancal.cpp",
-    "source/i18n/inputext.cpp",
-    "source/i18n/islamcal.cpp",
-    "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",
-    "source/i18n/nfrs.cpp",
-    "source/i18n/nfrule.cpp",
-    "source/i18n/nfsubs.cpp",
-    "source/i18n/nortrans.cpp",
-    "source/i18n/nultrans.cpp",
-    "source/i18n/numfmt.cpp",
-    "source/i18n/numsys.cpp",
-    "source/i18n/olsontz.cpp",
-    "source/i18n/persncal.cpp",
-    "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",
-    "source/i18n/rbt_pars.cpp",
-    "source/i18n/rbt_rule.cpp",
-    "source/i18n/rbt_set.cpp",
-    "source/i18n/rbtz.cpp",
-    "source/i18n/regexcmp.cpp",
-    "source/i18n/regeximp.cpp",
-    "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",
-    "source/i18n/sortkey.cpp",
-    "source/i18n/strmatch.cpp",
-    "source/i18n/strrepl.cpp",
-    "source/i18n/stsearch.cpp",
-    "source/i18n/taiwncal.cpp",
-    "source/i18n/timezone.cpp",
-    "source/i18n/titletrn.cpp",
-    "source/i18n/tmunit.cpp",
-    "source/i18n/tmutamt.cpp",
-    "source/i18n/tmutfmt.cpp",
-    "source/i18n/tolowtrn.cpp",
-    "source/i18n/toupptrn.cpp",
-    "source/i18n/translit.cpp",
-    "source/i18n/transreg.cpp",
-    "source/i18n/tridpars.cpp",
-    "source/i18n/tzfmt.cpp",
-    "source/i18n/tzgnames.cpp",
-    "source/i18n/tznames.cpp",
-    "source/i18n/tznames_impl.cpp",
-    "source/i18n/tzrule.cpp",
-    "source/i18n/tztrans.cpp",
-    "source/i18n/ucal.cpp",
-    "source/i18n/ucln_in.cpp",
-    "source/i18n/ucol.cpp",
-    "source/i18n/ucoleitr.cpp",
-    "source/i18n/ucol_res.cpp",
-    "source/i18n/ucol_sit.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",
-    "source/i18n/uni2name.cpp",
-    "source/i18n/unum.cpp",
-    "source/i18n/unumsys.cpp",
-    "source/i18n/upluralrules.cpp",
-    "source/i18n/uregexc.cpp",
-    "source/i18n/uregex.cpp",
-    "source/i18n/uregion.cpp",
-    "source/i18n/usearch.cpp",
-    "source/i18n/uspoof_build.cpp",
-    "source/i18n/uspoof_conf.cpp",
-    "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",
-    "source/i18n/vzone.cpp",
-    "source/i18n/windtfmt.cpp",
-    "source/i18n/winnmfmt.cpp",
-    "source/i18n/wintzimpl.cpp",
-    "source/i18n/zonemeta.cpp",
-    "source/i18n/zrule.cpp",
-    "source/i18n/ztrans.cpp",
-  ]
-  defines = [ "U_I18N_IMPLEMENTATION" ]
-  deps = [
-    ":icuuc",
-  ]
-
-  # ICU uses RTTI, replace the default "no rtti" config.
-  configs -= [
-    "//build/config/compiler:no_rtti",  # ICU uses RTTI.
-    "//build/config/compiler:chromium_code",
-  ]
-  configs += [
-    "//build/config/compiler:rtti",
-    "//build/config/compiler:no_chromium_code",
-  ]
-
-  configs += [ ":icu_code" ]
-  public_configs = [ ":icu_config" ]
-
-  cflags = []
-  if (is_android || is_linux) {
-    cflags += [
-      # ICU uses its own deprecated functions.
-      "-Wno-deprecated-declarations",
-    ]
-  }
-  if (is_clang) {
-    # uspoof.h has a U_NAMESPACE_USE macro. That's a bug,
-    # the header should use U_NAMESPACE_BEGIN instead.
-    # http://bugs.icu-project.org/trac/ticket/9054
-    configs -= [ "//build/config/clang:extra_warnings" ]
-
-    cflags += [
-      "-Wno-header-hygiene",
-
-      # Looks like a real issue, see http://crbug.com/114660
-      "-Wno-return-type-c-linkage",
-    ]
-  }
-}
-
-component("icuuc") {
-  sources = [
-    "source/common/appendable.cpp",
-    "source/common/bmpset.cpp",
-    "source/common/brkeng.cpp",
-    "source/common/brkiter.cpp",
-    "source/common/bytestream.cpp",
-    "source/common/bytestriebuilder.cpp",
-    "source/common/bytestrie.cpp",
-    "source/common/bytestrieiterator.cpp",
-    "source/common/caniter.cpp",
-    "source/common/chariter.cpp",
-    "source/common/charstr.cpp",
-    "source/common/cmemory.c",
-    "source/common/cstring.c",
-    "source/common/cwchar.c",
-    "source/common/dictbe.cpp",
-    "source/common/dictionarydata.cpp",
-    "source/common/dtintrv.cpp",
-    "source/common/errorcode.cpp",
-    "source/common/filterednormalizer2.cpp",
-    "source/common/icudataver.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",
-    "source/common/locid.cpp",
-    "source/common/loclikely.cpp",
-    "source/common/locmap.c",
-    "source/common/locresdata.cpp",
-    "source/common/locutil.cpp",
-    "source/common/messagepattern.cpp",
-    "source/common/normalizer2.cpp",
-    "source/common/normalizer2impl.cpp",
-    "source/common/normlzr.cpp",
-    "source/common/parsepos.cpp",
-    "source/common/patternprops.cpp",
-    "source/common/propname.cpp",
-    "source/common/propsvec.c",
-    "source/common/punycode.cpp",
-    "source/common/putil.cpp",
-    "source/common/rbbi.cpp",
-    "source/common/rbbidata.cpp",
-    "source/common/rbbinode.cpp",
-    "source/common/rbbirb.cpp",
-    "source/common/rbbiscan.cpp",
-    "source/common/rbbisetb.cpp",
-    "source/common/rbbistbl.cpp",
-    "source/common/rbbitblb.cpp",
-    "source/common/resbund_cnv.cpp",
-    "source/common/resbund.cpp",
-    "source/common/ruleiter.cpp",
-    "source/common/schriter.cpp",
-    "source/common/serv.cpp",
-    "source/common/servlk.cpp",
-    "source/common/servlkf.cpp",
-    "source/common/servls.cpp",
-    "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",
-    "source/common/ubidi.c",
-    "source/common/ubidiln.c",
-    "source/common/ubidi_props.c",
-    "source/common/ubidiwrt.c",
-    "source/common/ubrk.cpp",
-    "source/common/ucase.cpp",
-    "source/common/ucasemap.cpp",
-    "source/common/ucasemap_titlecase_brkiter.cpp",
-    "source/common/ucat.c",
-    "source/common/uchar.c",
-    "source/common/ucharstriebuilder.cpp",
-    "source/common/ucharstrie.cpp",
-    "source/common/ucharstrieiterator.cpp",
-    "source/common/uchriter.cpp",
-    "source/common/ucln_cmn.cpp",
-    "source/common/ucmndata.c",
-    "source/common/ucnv2022.cpp",
-    "source/common/ucnv_bld.cpp",
-    "source/common/ucnvbocu.cpp",
-    "source/common/ucnv.c",
-    "source/common/ucnv_cb.c",
-    "source/common/ucnv_cnv.c",
-    "source/common/ucnv_ct.c",
-    "source/common/ucnvdisp.c",
-    "source/common/ucnv_err.c",
-    "source/common/ucnv_ext.cpp",
-    "source/common/ucnvhz.c",
-    "source/common/ucnv_io.cpp",
-    "source/common/ucnvisci.c",
-    "source/common/ucnvlat1.c",
-    "source/common/ucnv_lmb.c",
-    "source/common/ucnvmbcs.cpp",
-    "source/common/ucnvscsu.c",
-    "source/common/ucnvsel.cpp",
-    "source/common/ucnv_set.c",
-    "source/common/ucnv_u16.c",
-    "source/common/ucnv_u32.c",
-    "source/common/ucnv_u7.c",
-    "source/common/ucnv_u8.c",
-    "source/common/ucol_swp.cpp",
-    "source/common/udata.cpp",
-    "source/common/udatamem.c",
-    "source/common/udataswp.c",
-    "source/common/uenum.c",
-    "source/common/uhash.c",
-    "source/common/uhash_us.cpp",
-    "source/common/uidna.cpp",
-    "source/common/uinit.cpp",
-    "source/common/uinvchar.c",
-    "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",
-    "source/common/uniset.cpp",
-    "source/common/uniset_props.cpp",
-    "source/common/unisetspan.cpp",
-    "source/common/unistr_case.cpp",
-    "source/common/unistr_case_locale.cpp",
-    "source/common/unistr_cnv.cpp",
-    "source/common/unistr.cpp",
-    "source/common/unistr_props.cpp",
-    "source/common/unistr_titlecase_brkiter.cpp",
-    "source/common/unormcmp.cpp",
-    "source/common/unorm.cpp",
-    "source/common/uobject.cpp",
-    "source/common/uprops.cpp",
-    "source/common/uresbund.cpp",
-    "source/common/ures_cnv.c",
-    "source/common/uresdata.c",
-    "source/common/usc_impl.c",
-    "source/common/uscript.c",
-    "source/common/uscript_props.cpp",
-    "source/common/uset.cpp",
-    "source/common/usetiter.cpp",
-    "source/common/uset_props.cpp",
-    "source/common/ushape.cpp",
-    "source/common/usprep.cpp",
-    "source/common/ustack.cpp",
-    "source/common/ustrcase.cpp",
-    "source/common/ustrcase_locale.cpp",
-    "source/common/ustr_cnv.cpp",
-    "source/common/ustrenum.cpp",
-    "source/common/ustrfmt.c",
-    "source/common/ustring.cpp",
-    "source/common/ustr_titlecase_brkiter.cpp",
-    "source/common/ustrtrns.cpp",
-    "source/common/ustr_wcs.cpp",
-    "source/common/utext.cpp",
-    "source/common/utf_impl.c",
-    "source/common/util.cpp",
-    "source/common/util_props.cpp",
-    "source/common/utrace.c",
-    "source/common/utrie2_builder.cpp",
-    "source/common/utrie2.cpp",
-    "source/common/utrie.cpp",
-    "source/common/uts46.cpp",
-    "source/common/utypes.c",
-    "source/common/uvector.cpp",
-    "source/common/uvectr32.cpp",
-    "source/common/uvectr64.cpp",
-    "source/common/wintz.c",
-  ]
-  defines = [ "U_COMMON_IMPLEMENTATION" ]
-  deps = [
-    ":icudata",
-  ]
-  configs += [ ":icu_code" ]
-
-  configs -= [
-    "//build/config/compiler:no_rtti",  # ICU uses RTTI.
-    "//build/config/compiler:chromium_code",
-  ]
-  configs += [
-    "//build/config/compiler:rtti",
-    "//build/config/compiler:no_chromium_code",
-  ]
-
-  public_configs = [ ":icu_config" ]
-
-  if (is_win || icu_use_data_file) {
-    sources += [ "source/stubdata/stubdata.c" ]
-    defines += [ "U_ICUDATAENTRY_IN_COMMON" ]
-  }
-}
-
-# TODO(GYP) support use_system_icu.
-if (icu_use_data_file) {
-  if (is_ios) {
-    # TODO(GYP): Support mac resource bundle shown below.
-    # 'link_settings': {
-    #   'mac_bundle_resources': [
-    #     'source/data/in/icudtl.dat',
-    #   ],
-    # }
-  } else {
-    copy("icudata") {
-      if (is_android) {
-        sources = [
-          "android/icudtl.dat",
-        ]
-      } else {
-        sources = [
-          "source/data/in/icudtl.dat",
-        ]
-      }
-
-      outputs = [
-        "$root_out_dir/icudtl.dat",
-      ]
-    }
-  }
-} else {
-  if (is_win) {
-    # On Windows the target DLL is pre-built so just use a copy rule.
-    copy("icudata") {
-      sources = [
-        "windows/icudt.dll",
-      ]
-      outputs = [
-        "$root_out_dir/icudt.dll",
-      ]
-    }
-  } else {
-    source_set("icudata") {
-      # These are hand-generated, but will do for now.
-      #
-      # TODO(GYP): Gyp has considerations here for QNX and for the host toolchain
-      #  that have not been ported over.
-      if (is_linux) {
-        sources = [
-          "linux/icudtl_dat.S",
-        ]
-      } else if (is_mac) {
-        sources = [
-          "mac/icudtl_dat.S",
-        ]
-      } else if (is_android) {
-        sources = [
-          "android/icudtl_dat.S",
-        ]
-      } else {
-        assert(false, "No icu data for this platform")
-      }
-      defines = [ "U_HIDE_DATA_SYMBOL" ]
-    }
-  }
-}
diff --git a/build/secondary/third_party/icu/config.gni b/build/secondary/third_party/icu/config.gni
deleted file mode 100644
index 9c389de..0000000
--- a/build/secondary/third_party/icu/config.gni
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
-
-declare_args() {
-  # Tells icu to load an external data file rather than rely on the icudata
-  # being linked directly into the binary.
-  #
-  # This flag is a bit confusing. As of this writing, icu.gyp set the value to
-  # 0 but common.gypi sets the value to 1 for most platforms (and the 1 takes
-  # precedence).
-  #
-  # TODO(GYP) We'll probably need to enhance this logic to set the value to
-  # true or false in similar circumstances.
-  icu_use_data_file = true
-}
diff --git a/build/secondary/tools/grit/grit_rule.gni b/build/secondary/tools/grit/grit_rule.gni
index 777c3dc..5d3b1e4 100644
--- a/build/secondary/tools/grit/grit_rule.gni
+++ b/build/secondary/tools/grit/grit_rule.gni
@@ -285,6 +285,12 @@
     "enable_service_discovery",
   ]
 }
+if (mac_views_browser) {
+  grit_defines += [
+    "-D",
+    "mac_views_browser",
+  ]
+}
 
 grit_resource_id_file = "//tools/gritsettings/resource_ids"
 grit_info_script = "//tools/grit/grit_info.py"
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index ad33651..b4e5ffa 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -418,6 +418,10 @@
     "resources/software_rasterizer.h",
     "resources/task_graph_runner.cc",
     "resources/task_graph_runner.h",
+    "resources/texture_compressor.cc",
+    "resources/texture_compressor.h",
+    "resources/texture_compressor_etc1.cc",
+    "resources/texture_compressor_etc1.h",
     "resources/texture_mailbox.cc",
     "resources/texture_mailbox.h",
     "resources/texture_mailbox_deleter.cc",
@@ -655,6 +659,8 @@
     "test/test_occlusion_tracker.h",
     "test/test_shared_bitmap_manager.cc",
     "test/test_shared_bitmap_manager.h",
+    "test/test_task_graph_runner.cc",
+    "test/test_task_graph_runner.h",
     "test/test_texture.cc",
     "test/test_texture.h",
     "test/test_tile_priorities.cc",
@@ -860,6 +866,7 @@
     "resources/picture_layer_tiling_perftest.cc",
     "resources/picture_pile_impl_perftest.cc",
     "resources/task_graph_runner_perftest.cc",
+    "resources/texture_compressor_perftest.cc",
     "resources/tile_manager_perftest.cc",
     "resources/tile_task_worker_pool_perftest.cc",
     "test/cc_test_suite.cc",
diff --git a/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc b/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
index f7a5878..6d2d3e8 100644
--- a/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
+++ b/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
@@ -9,6 +9,7 @@
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,7 +21,7 @@
       public ScrollbarAnimationControllerClient {
  public:
   ScrollbarAnimationControllerLinearFadeTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_) {}
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {}
 
   void StartAnimatingScrollbarAnimationController(
       ScrollbarAnimationController* controller) override {
@@ -75,6 +76,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   scoped_ptr<ScrollbarAnimationControllerLinearFade> scrollbar_controller_;
   scoped_ptr<LayerImpl> clip_layer_;
diff --git a/cc/animation/scrollbar_animation_controller_thinning_unittest.cc b/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
index 01763c9..04f81d3 100644
--- a/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
+++ b/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
@@ -9,6 +9,7 @@
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,7 +21,7 @@
       public ScrollbarAnimationControllerClient {
  public:
   ScrollbarAnimationControllerThinningTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_) {}
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {}
 
   void StartAnimatingScrollbarAnimationController(
       ScrollbarAnimationController* controller) override {
@@ -74,6 +75,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   scoped_ptr<ScrollbarAnimationControllerThinning> scrollbar_controller_;
   scoped_ptr<LayerImpl> clip_layer_;
diff --git a/cc/base/tiling_data.cc b/cc/base/tiling_data.cc
index 95fd68b..c9b2fd9 100644
--- a/cc/base/tiling_data.cc
+++ b/cc/base/tiling_data.cc
@@ -350,7 +350,11 @@
   return *this;
 }
 
-TilingData::DifferenceIterator::DifferenceIterator(
+TilingData::BaseDifferenceIterator::BaseDifferenceIterator() {
+  done();
+}
+
+TilingData::BaseDifferenceIterator::BaseDifferenceIterator(
     const TilingData* tiling_data,
     const gfx::Rect& consider_rect,
     const gfx::Rect& ignore_rect)
@@ -369,9 +373,8 @@
 
   gfx::Rect tiling_bounds_rect(tiling_data->tiling_size());
   gfx::Rect consider(consider_rect);
-  gfx::Rect ignore(ignore_rect);
   consider.Intersect(tiling_bounds_rect);
-  ignore.Intersect(tiling_bounds_rect);
+
   if (consider.IsEmpty()) {
     done();
     return;
@@ -382,6 +385,9 @@
   consider_right_ = tiling_data->TileXIndexFromSrcCoord(consider.right() - 1);
   consider_bottom_ = tiling_data->TileYIndexFromSrcCoord(consider.bottom() - 1);
 
+  gfx::Rect ignore(ignore_rect);
+  ignore.Intersect(tiling_bounds_rect);
+
   if (!ignore.IsEmpty()) {
     ignore_left_ = tiling_data->TileXIndexFromSrcCoord(ignore.x());
     ignore_top_ = tiling_data->TileYIndexFromSrcCoord(ignore.y());
@@ -393,10 +399,31 @@
     ignore_top_ = std::max(ignore_top_, consider_top_);
     ignore_right_ = std::min(ignore_right_, consider_right_);
     ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
-  }
 
-  if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
-      ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+    if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
+        ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+      consider_left_ = consider_top_ = consider_right_ = consider_bottom_ = -1;
+      done();
+      return;
+    }
+  }
+}
+
+bool TilingData::BaseDifferenceIterator::HasConsiderRect() const {
+  // Consider indices are either all valid or all equal to -1.
+  DCHECK((0 <= consider_left_ && consider_left_ <= consider_right_ &&
+          0 <= consider_top_ && consider_top_ <= consider_bottom_) ||
+         (consider_left_ == -1 && consider_top_ == -1 &&
+          consider_right_ == -1 && consider_bottom_ == -1));
+  return consider_left_ != -1;
+}
+
+TilingData::DifferenceIterator::DifferenceIterator(
+    const TilingData* tiling_data,
+    const gfx::Rect& consider_rect,
+    const gfx::Rect& ignore_rect)
+    : BaseDifferenceIterator(tiling_data, consider_rect, ignore_rect) {
+  if (!HasConsiderRect()) {
     done();
     return;
   }
@@ -446,82 +473,40 @@
     const gfx::Rect& consider_rect,
     const gfx::Rect& ignore_rect,
     const gfx::Rect& center_rect)
-    : consider_left_(-1),
-      consider_top_(-1),
-      consider_right_(-1),
-      consider_bottom_(-1),
-      ignore_left_(-1),
-      ignore_top_(-1),
-      ignore_right_(-1),
-      ignore_bottom_(-1),
+    : BaseDifferenceIterator(tiling_data, consider_rect, ignore_rect),
       direction_(RIGHT),
       delta_x_(1),
       delta_y_(0),
       current_step_(0),
       horizontal_step_count_(0),
       vertical_step_count_(0) {
-  if (tiling_data->num_tiles_x() <= 0 || tiling_data->num_tiles_y() <= 0) {
-    done();
-    return;
-  }
-
-  gfx::Rect tiling_bounds_rect(tiling_data->tiling_size());
-  gfx::Rect consider(consider_rect);
-  gfx::Rect ignore(ignore_rect);
-  gfx::Rect center(center_rect);
-  consider.Intersect(tiling_bounds_rect);
-  ignore.Intersect(tiling_bounds_rect);
-  if (consider.IsEmpty()) {
-    done();
-    return;
-  }
-
-  consider_left_ = tiling_data->TileXIndexFromSrcCoord(consider.x());
-  consider_top_ = tiling_data->TileYIndexFromSrcCoord(consider.y());
-  consider_right_ = tiling_data->TileXIndexFromSrcCoord(consider.right() - 1);
-  consider_bottom_ = tiling_data->TileYIndexFromSrcCoord(consider.bottom() - 1);
-
-  if (!ignore.IsEmpty()) {
-    ignore_left_ = tiling_data->TileXIndexFromSrcCoord(ignore.x());
-    ignore_top_ = tiling_data->TileYIndexFromSrcCoord(ignore.y());
-    ignore_right_ = tiling_data->TileXIndexFromSrcCoord(ignore.right() - 1);
-    ignore_bottom_ = tiling_data->TileYIndexFromSrcCoord(ignore.bottom() - 1);
-
-    // Clamp ignore indices to consider indices.
-    ignore_left_ = std::max(ignore_left_, consider_left_);
-    ignore_top_ = std::max(ignore_top_, consider_top_);
-    ignore_right_ = std::min(ignore_right_, consider_right_);
-    ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
-  }
-
-  if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
-      ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+  if (!HasConsiderRect()) {
     done();
     return;
   }
 
   // Determine around left, such that it is between -1 and num_tiles_x.
   int around_left = 0;
-  if (center.x() < 0 || center.IsEmpty())
+  if (center_rect.x() < 0 || center_rect.IsEmpty())
     around_left = -1;
-  else if (center.x() >= tiling_data->tiling_size().width())
+  else if (center_rect.x() >= tiling_data->tiling_size().width())
     around_left = tiling_data->num_tiles_x();
   else
-    around_left = tiling_data->TileXIndexFromSrcCoord(center.x());
+    around_left = tiling_data->TileXIndexFromSrcCoord(center_rect.x());
 
   // Determine around top, such that it is between -1 and num_tiles_y.
   int around_top = 0;
-  if (center.y() < 0 || center.IsEmpty())
+  if (center_rect.y() < 0 || center_rect.IsEmpty())
     around_top = -1;
-  else if (center.y() >= tiling_data->tiling_size().height())
+  else if (center_rect.y() >= tiling_data->tiling_size().height())
     around_top = tiling_data->num_tiles_y();
   else
-    around_top = tiling_data->TileYIndexFromSrcCoord(center.y());
+    around_top = tiling_data->TileYIndexFromSrcCoord(center_rect.y());
 
   // Determine around right, such that it is between -1 and num_tiles_x.
-  int right_src_coord = center.right() - 1;
+  int right_src_coord = center_rect.right() - 1;
   int around_right = 0;
-  if (right_src_coord < 0 || center.IsEmpty()) {
+  if (right_src_coord < 0 || center_rect.IsEmpty()) {
     around_right = -1;
   } else if (right_src_coord >= tiling_data->tiling_size().width()) {
     around_right = tiling_data->num_tiles_x();
@@ -530,9 +515,9 @@
   }
 
   // Determine around bottom, such that it is between -1 and num_tiles_y.
-  int bottom_src_coord = center.bottom() - 1;
+  int bottom_src_coord = center_rect.bottom() - 1;
   int around_bottom = 0;
-  if (bottom_src_coord < 0 || center.IsEmpty()) {
+  if (bottom_src_coord < 0 || center_rect.IsEmpty()) {
     around_bottom = -1;
   } else if (bottom_src_coord >= tiling_data->tiling_size().height()) {
     around_bottom = tiling_data->num_tiles_y();
@@ -669,83 +654,41 @@
     const gfx::Rect& consider_rect,
     const gfx::Rect& ignore_rect,
     const gfx::Rect& center_rect)
-    : consider_left_(-1),
-      consider_top_(-1),
-      consider_right_(-1),
-      consider_bottom_(-1),
+    : BaseDifferenceIterator(tiling_data, consider_rect, ignore_rect),
       around_left_(-1),
       around_top_(-1),
       around_right_(-1),
       around_bottom_(-1),
-      ignore_left_(-1),
-      ignore_top_(-1),
-      ignore_right_(-1),
-      ignore_bottom_(-1),
       direction_(LEFT),
       delta_x_(-1),
       delta_y_(0),
       current_step_(0),
       horizontal_step_count_(0),
       vertical_step_count_(0) {
-  if (tiling_data->num_tiles_x() <= 0 || tiling_data->num_tiles_y() <= 0) {
-    done();
-    return;
-  }
-
-  gfx::Rect tiling_bounds_rect(tiling_data->tiling_size());
-  gfx::Rect consider(consider_rect);
-  gfx::Rect ignore(ignore_rect);
-  gfx::Rect center(center_rect);
-  consider.Intersect(tiling_bounds_rect);
-  ignore.Intersect(tiling_bounds_rect);
-  if (consider.IsEmpty()) {
-    done();
-    return;
-  }
-
-  consider_left_ = tiling_data->TileXIndexFromSrcCoord(consider.x());
-  consider_top_ = tiling_data->TileYIndexFromSrcCoord(consider.y());
-  consider_right_ = tiling_data->TileXIndexFromSrcCoord(consider.right() - 1);
-  consider_bottom_ = tiling_data->TileYIndexFromSrcCoord(consider.bottom() - 1);
-
-  if (!ignore.IsEmpty()) {
-    ignore_left_ = tiling_data->TileXIndexFromSrcCoord(ignore.x());
-    ignore_top_ = tiling_data->TileYIndexFromSrcCoord(ignore.y());
-    ignore_right_ = tiling_data->TileXIndexFromSrcCoord(ignore.right() - 1);
-    ignore_bottom_ = tiling_data->TileYIndexFromSrcCoord(ignore.bottom() - 1);
-
-    // Clamp ignore indices to consider indices.
-    ignore_left_ = std::max(ignore_left_, consider_left_);
-    ignore_top_ = std::max(ignore_top_, consider_top_);
-    ignore_right_ = std::min(ignore_right_, consider_right_);
-    ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
-  }
-
-  if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
-      ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+  if (!HasConsiderRect()) {
     done();
     return;
   }
 
   // Determine around left, such that it is between -1 and num_tiles_x.
-  if (center.x() < 0 || center.IsEmpty())
+  if (center_rect.x() < 0 || center_rect.IsEmpty())
     around_left_ = -1;
-  else if (center.x() >= tiling_data->tiling_size().width())
+  else if (center_rect.x() >= tiling_data->tiling_size().width())
     around_left_ = tiling_data->num_tiles_x();
   else
-    around_left_ = tiling_data->TileXIndexFromSrcCoord(center.x());
+    around_left_ = tiling_data->TileXIndexFromSrcCoord(center_rect.x());
 
   // Determine around top, such that it is between -1 and num_tiles_y.
-  if (center.y() < 0 || center.IsEmpty())
+  if (center_rect.y() < 0 || center_rect.IsEmpty())
     around_top_ = -1;
-  else if (center.y() >= tiling_data->tiling_size().height())
+  else if (center_rect.y() >= tiling_data->tiling_size().height())
     around_top_ = tiling_data->num_tiles_y();
   else
-    around_top_ = tiling_data->TileYIndexFromSrcCoord(center.y());
+    around_top_ = tiling_data->TileYIndexFromSrcCoord(center_rect.y());
 
   // Determine around right, such that it is between -1 and num_tiles_x.
-  int right_src_coord = center.right() - 1;
-  if (right_src_coord < 0 || center.IsEmpty()) {
+  int right_src_coord = center_rect.right() - 1;
+  if (right_src_coord < 0 || center_rect.IsEmpty()) {
     around_right_ = -1;
   } else if (right_src_coord >= tiling_data->tiling_size().width()) {
     around_right_ = tiling_data->num_tiles_x();
@@ -754,8 +697,8 @@
   }
 
   // Determine around bottom, such that it is between -1 and num_tiles_y.
-  int bottom_src_coord = center.bottom() - 1;
-  if (bottom_src_coord < 0 || center.IsEmpty()) {
+  int bottom_src_coord = center_rect.bottom() - 1;
+  if (bottom_src_coord < 0 || center_rect.IsEmpty()) {
     around_bottom_ = -1;
   } else if (bottom_src_coord >= tiling_data->tiling_size().height()) {
     around_bottom_ = tiling_data->num_tiles_y();
diff --git a/cc/base/tiling_data.h b/cc/base/tiling_data.h
index 45b763e..a35be59 100644
--- a/cc/base/tiling_data.h
+++ b/cc/base/tiling_data.h
@@ -101,19 +101,21 @@
     int bottom_;
   };
 
-  // Iterate through all indices whose bounds (not including borders) intersect
-  // with |consider| but which also do not intersect with |ignore|.
-  class CC_EXPORT DifferenceIterator : public BaseIterator {
-   public:
-    DifferenceIterator(const TilingData* tiling_data,
-                       const gfx::Rect& consider_rect,
-                       const gfx::Rect& ignore_rect);
-    DifferenceIterator& operator++();
+  class CC_EXPORT BaseDifferenceIterator : public BaseIterator {
+   protected:
+    BaseDifferenceIterator();
+    BaseDifferenceIterator(const TilingData* tiling_data,
+                           const gfx::Rect& consider_rect,
+                           const gfx::Rect& ignore_rect);
 
-   private:
+    bool HasConsiderRect() const;
+    bool in_consider_rect() const {
+      return index_x_ >= consider_left_ && index_x_ <= consider_right_ &&
+             index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
+    }
     bool in_ignore_rect() const {
-     return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
-       index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
+      return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
+             index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
     }
 
     int consider_left_;
@@ -126,10 +128,20 @@
     int ignore_bottom_;
   };
 
+  // Iterate through all indices whose bounds (not including borders) intersect
+  // with |consider| but which also do not intersect with |ignore|.
+  class CC_EXPORT DifferenceIterator : public BaseDifferenceIterator {
+   public:
+    DifferenceIterator(const TilingData* tiling_data,
+                       const gfx::Rect& consider_rect,
+                       const gfx::Rect& ignore_rect);
+    DifferenceIterator& operator++();
+  };
+
   // Iterate through all indices whose bounds + border intersect with
   // |consider| but which also do not intersect with |ignore|. The iterator
   // order is a counterclockwise spiral around the given center.
-  class CC_EXPORT SpiralDifferenceIterator : public BaseIterator {
+  class CC_EXPORT SpiralDifferenceIterator : public BaseDifferenceIterator {
    public:
     SpiralDifferenceIterator();
     SpiralDifferenceIterator(const TilingData* tiling_data,
@@ -139,14 +151,6 @@
     SpiralDifferenceIterator& operator++();
 
    private:
-    bool in_consider_rect() const {
-      return index_x_ >= consider_left_ && index_x_ <= consider_right_ &&
-             index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
-    }
-    bool in_ignore_rect() const {
-      return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
-             index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
-    }
     bool valid_column() const {
       return index_x_ >= consider_left_ && index_x_ <= consider_right_;
     }
@@ -162,15 +166,6 @@
     bool needs_direction_switch() const;
     void switch_direction();
 
-    int consider_left_;
-    int consider_top_;
-    int consider_right_;
-    int consider_bottom_;
-    int ignore_left_;
-    int ignore_top_;
-    int ignore_right_;
-    int ignore_bottom_;
-
     enum Direction { UP, LEFT, DOWN, RIGHT };
 
     Direction direction_;
@@ -181,7 +176,8 @@
     int vertical_step_count_;
   };
 
-  class CC_EXPORT ReverseSpiralDifferenceIterator : public BaseIterator {
+  class CC_EXPORT ReverseSpiralDifferenceIterator
+      : public BaseDifferenceIterator {
    public:
     ReverseSpiralDifferenceIterator();
     ReverseSpiralDifferenceIterator(const TilingData* tiling_data,
@@ -191,18 +187,10 @@
     ReverseSpiralDifferenceIterator& operator++();
 
    private:
-    bool in_consider_rect() const {
-      return index_x_ >= consider_left_ && index_x_ <= consider_right_ &&
-             index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
-    }
     bool in_around_rect() const {
       return index_x_ >= around_left_ && index_x_ <= around_right_ &&
              index_y_ >= around_top_ && index_y_ <= around_bottom_;
     }
-    bool in_ignore_rect() const {
-      return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
-             index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
-    }
     bool valid_column() const {
       return index_x_ >= consider_left_ && index_x_ <= consider_right_;
     }
@@ -218,18 +206,10 @@
     bool needs_direction_switch() const;
     void switch_direction();
 
-    int consider_left_;
-    int consider_top_;
-    int consider_right_;
-    int consider_bottom_;
     int around_left_;
     int around_top_;
     int around_right_;
     int around_bottom_;
-    int ignore_left_;
-    int ignore_top_;
-    int ignore_right_;
-    int ignore_bottom_;
 
     enum Direction { LEFT, UP, RIGHT, DOWN };
 
diff --git a/cc/cc.gyp b/cc/cc.gyp
index c796e7d..c5f2033 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -478,6 +478,10 @@
         'resources/software_rasterizer.h',
         'resources/task_graph_runner.cc',
         'resources/task_graph_runner.h',
+        'resources/texture_compressor.cc',
+        'resources/texture_compressor.h',
+        'resources/texture_compressor_etc1.cc',
+        'resources/texture_compressor_etc1.h',
         'resources/texture_mailbox.cc',
         'resources/texture_mailbox.h',
         'resources/texture_mailbox_deleter.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index bafd747..cb84033 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -260,6 +260,8 @@
       'test/test_occlusion_tracker.h',
       'test/test_shared_bitmap_manager.cc',
       'test/test_shared_bitmap_manager.h',
+      'test/test_task_graph_runner.cc',
+      'test/test_task_graph_runner.h',
       'test/test_texture.cc',
       'test/test_texture.h',
       'test/test_tile_priorities.cc',
@@ -351,6 +353,7 @@
         'resources/picture_layer_tiling_perftest.cc',
         'resources/picture_pile_impl_perftest.cc',
         'resources/task_graph_runner_perftest.cc',
+        'resources/texture_compressor_perftest.cc',
         'resources/tile_manager_perftest.cc',
         'resources/tile_task_worker_pool_perftest.cc',
         'test/cc_test_suite.cc',
diff --git a/cc/cc_unittests.isolate b/cc/cc_unittests.isolate
index dc6c449..7c359d0 100644
--- a/cc/cc_unittests.isolate
+++ b/cc/cc_unittests.isolate
@@ -84,5 +84,6 @@
   ],
   'includes': [
     '../base/base.isolate',
+    '../third_party/angle/angle.isolate',
   ],
 }
diff --git a/cc/debug/frame_viewer_instrumentation.cc b/cc/debug/frame_viewer_instrumentation.cc
index 53c1551..226cc88 100644
--- a/cc/debug/frame_viewer_instrumentation.cc
+++ b/cc/debug/frame_viewer_instrumentation.cc
@@ -6,6 +6,12 @@
 
 namespace cc {
 namespace frame_viewer_instrumentation {
+
+const char kCategoryLayerTree[] =
+    TRACE_DISABLED_BY_DEFAULT("cc.debug") ","
+    TRACE_DISABLED_BY_DEFAULT("cc.debug.quads") ","
+    TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers");
+
 namespace {
 
 const char kCategory[] = "cc," TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
@@ -60,5 +66,11 @@
   TRACE_EVENT_END0(kCategory, kRasterTask);
 }
 
+bool IsTracingLayerTreeSnapshots() {
+  bool category_enabled;
+  TRACE_EVENT_CATEGORY_GROUP_ENABLED(kCategoryLayerTree, &category_enabled);
+  return category_enabled;
+}
+
 }  // namespace frame_viewer_instrumentation
 }  // namespace cc
diff --git a/cc/debug/frame_viewer_instrumentation.h b/cc/debug/frame_viewer_instrumentation.h
index 762d058..b6736be 100644
--- a/cc/debug/frame_viewer_instrumentation.h
+++ b/cc/debug/frame_viewer_instrumentation.h
@@ -11,6 +11,8 @@
 namespace cc {
 namespace frame_viewer_instrumentation {
 
+extern const char kCategoryLayerTree[];
+
 class ScopedAnalyzeTask {
  public:
   ScopedAnalyzeTask(const void* tile_id,
@@ -35,6 +37,8 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedRasterTask);
 };
 
+bool IsTracingLayerTreeSnapshots();
+
 }  // namespace frame_viewer_instrumentation
 }  // namespace cc
 
diff --git a/cc/debug/micro_benchmark_controller_unittest.cc b/cc/debug/micro_benchmark_controller_unittest.cc
index 31494ac..2a8dce4 100644
--- a/cc/debug/micro_benchmark_controller_unittest.cc
+++ b/cc/debug/micro_benchmark_controller_unittest.cc
@@ -25,7 +25,7 @@
     impl_proxy_ = make_scoped_ptr(new FakeImplProxy);
     shared_bitmap_manager_.reset(new TestSharedBitmapManager());
     layer_tree_host_impl_ = make_scoped_ptr(new FakeLayerTreeHostImpl(
-        impl_proxy_.get(), shared_bitmap_manager_.get()));
+        impl_proxy_.get(), shared_bitmap_manager_.get(), nullptr));
 
     layer_tree_host_ = FakeLayerTreeHost::Create(&layer_tree_host_client_);
     layer_tree_host_->SetRootLayer(Layer::Create());
diff --git a/cc/debug/rasterize_and_record_benchmark.cc b/cc/debug/rasterize_and_record_benchmark.cc
index e46dcfe..73a0fa5 100644
--- a/cc/debug/rasterize_and_record_benchmark.cc
+++ b/cc/debug/rasterize_and_record_benchmark.cc
@@ -225,9 +225,11 @@
         min_time = duration;
     }
 
-    record_results_.bytes_used += memory_used;
-    record_results_.pixels_recorded +=
-        visible_content_rect.width() * visible_content_rect.height();
+    if (mode_index == RecordingSource::RECORD_NORMALLY) {
+      record_results_.bytes_used += memory_used;
+      record_results_.pixels_recorded +=
+          visible_content_rect.width() * visible_content_rect.height();
+    }
     record_results_.total_best_time[mode_index] += min_time;
   }
 }
diff --git a/cc/input/top_controls_manager_unittest.cc b/cc/input/top_controls_manager_unittest.cc
index 4145cac..52e46e6 100644
--- a/cc/input/top_controls_manager_unittest.cc
+++ b/cc/input/top_controls_manager_unittest.cc
@@ -15,6 +15,7 @@
 #include "cc/test/fake_impl_proxy.h"
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/frame_time.h"
@@ -28,7 +29,7 @@
   MockTopControlsManagerClient(float top_controls_height,
                                float top_controls_show_threshold,
                                float top_controls_hide_threshold)
-      : host_impl_(&proxy_, &shared_bitmap_manager_),
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
         redraw_needed_(false),
         update_draw_properties_needed_(false),
         top_controls_shown_ratio_(1.f),
@@ -83,6 +84,7 @@
  private:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   scoped_ptr<LayerTreeImpl> active_tree_;
   scoped_ptr<LayerImpl> root_scroll_layer_;
diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc
index 87e559a..9e2d553 100644
--- a/cc/layers/delegated_renderer_layer_impl_unittest.cc
+++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "cc/test/render_pass_test_common.h"
 #include "cc/test/render_pass_test_utils.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -37,8 +38,8 @@
     LayerTreeSettings settings;
     settings.minimum_occlusion_tracking_size = gfx::Size();
 
-    host_impl_.reset(
-        new FakeLayerTreeHostImpl(settings, &proxy_, &shared_bitmap_manager_));
+    host_impl_.reset(new FakeLayerTreeHostImpl(
+        settings, &proxy_, &shared_bitmap_manager_, &task_graph_runner_));
     host_impl_->InitializeRenderer(FakeOutputSurface::Create3d());
     host_impl_->SetViewportSize(gfx::Size(10, 10));
   }
@@ -48,6 +49,7 @@
   DebugScopedSetImplThreadAndMainThreadBlocked
       always_impl_thread_and_main_thread_blocked_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   scoped_ptr<LayerTreeHostImpl> host_impl_;
 };
 
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index e1a9daf..d2558c1 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -223,6 +223,10 @@
   resources_.clear();
 }
 
+gfx::Rect HeadsUpDisplayLayerImpl::GetEnclosingRectInTargetSpace() const {
+  return GetScaledEnclosingRectInTargetSpace(internal_contents_scale_);
+}
+
 void HeadsUpDisplayLayerImpl::UpdateHudContents() {
   const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state();
 
@@ -551,23 +555,23 @@
   SkColor color = SK_ColorRED;
   switch (layer_tree_impl()->GetGpuRasterizationStatus()) {
     case GpuRasterizationStatus::ON:
-      status = "GPU raster: on";
+      status = "on";
       color = SK_ColorGREEN;
       break;
     case GpuRasterizationStatus::ON_FORCED:
-      status = "GPU raster: on (forced)";
+      status = "on (forced)";
       color = SK_ColorGREEN;
       break;
     case GpuRasterizationStatus::OFF_DEVICE:
-      status = "GPU raster: off (device)";
+      status = "off (device)";
       color = SK_ColorRED;
       break;
     case GpuRasterizationStatus::OFF_VIEWPORT:
-      status = "GPU raster: off (viewport)";
+      status = "off (viewport)";
       color = SK_ColorYELLOW;
       break;
     case GpuRasterizationStatus::OFF_CONTENT:
-      status = "GPU raster: off (content)";
+      status = "off (content)";
       color = SK_ColorYELLOW;
       break;
   }
@@ -578,17 +582,20 @@
   const int kPadding = 4;
   const int kFontHeight = 13;
 
-  const int height = kFontHeight + 2 * kPadding;
+  const int height = 2 * kFontHeight + 3 * kPadding;
   const int left = bounds().width() - width - right;
   const SkRect area = SkRect::MakeXYWH(left, top, width, height);
 
   SkPaint paint = CreatePaint();
   DrawGraphBackground(canvas, &paint, area);
 
-  SkPoint gpu_status_pos = SkPoint::Make(left + kPadding, top + kFontHeight);
+  SkPoint gpu_status_pos = SkPoint::Make(left + width - kPadding,
+                                         top + 2 * kFontHeight + 2 * kPadding);
 
   paint.setColor(color);
-  DrawText(canvas, &paint, status, SkPaint::kLeft_Align, kFontHeight,
+  DrawText(canvas, &paint, "GPU raster: ", SkPaint::kLeft_Align, kFontHeight,
+           left + kPadding, top + kFontHeight + kPadding);
+  DrawText(canvas, &paint, status, SkPaint::kRight_Align, kFontHeight,
            gpu_status_pos);
 
   return area;
@@ -600,7 +607,7 @@
     int right,
     int top) const {
   const int kPadding = 4;
-  const int kFontHeight = 15;
+  const int kFontHeight = 14;
 
   const int kGraphWidth = paint_time_counter->HistorySize();
   const int kGraphHeight = 40;
@@ -632,7 +639,7 @@
       "%.1f-%.1f", paint_time_graph_.min, paint_time_graph_.max);
 
   paint.setColor(DebugColors::PaintTimeDisplayTextAndGraphColor());
-  DrawText(canvas, &paint, "Compositor frame time (ms)", SkPaint::kLeft_Align,
+  DrawText(canvas, &paint, "Compositor frame time(ms)", SkPaint::kLeft_Align,
            kFontHeight, text_bounds.left(), text_bounds.bottom());
   DrawText(canvas,
            &paint,
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
index 54bf153..750d991 100644
--- a/cc/layers/heads_up_display_layer_impl.h
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -45,6 +45,8 @@
 
   void ReleaseResources() override;
 
+  gfx::Rect GetEnclosingRectInTargetSpace() const override;
+
   bool IsAnimatingHUDContents() const { return fade_step_ > 0; }
 
  private:
diff --git a/cc/layers/heads_up_display_layer_impl_unittest.cc b/cc/layers/heads_up_display_layer_impl_unittest.cc
index 4b07323..ab38fdd 100644
--- a/cc/layers/heads_up_display_layer_impl_unittest.cc
+++ b/cc/layers/heads_up_display_layer_impl_unittest.cc
@@ -32,7 +32,7 @@
 TEST(HeadsUpDisplayLayerImplTest, ResourcelessSoftwareDrawAfterResourceLoss) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   host_impl.InitializeRenderer(FakeOutputSurface::Create3d());
   scoped_ptr<HeadsUpDisplayLayerImpl> layer =
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 12773a1..1c677b6 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -18,6 +18,7 @@
 #include "cc/animation/keyframed_animation_curve.h"
 #include "cc/animation/layer_animation_controller.h"
 #include "cc/base/simple_enclosed_region.h"
+#include "cc/debug/frame_viewer_instrumentation.h"
 #include "cc/layers/layer_client.h"
 #include "cc/layers/layer_impl.h"
 #include "cc/layers/scrollbar_layer_interface.h"
@@ -897,12 +898,7 @@
   layer->SetContentBounds(content_bounds());
   layer->SetContentsScale(contents_scale_x(), contents_scale_y());
 
-  bool is_tracing;
-  TRACE_EVENT_CATEGORY_GROUP_ENABLED(
-      TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
-          "devtools.timeline.layers"),
-      &is_tracing);
-  if (is_tracing)
+  if (frame_viewer_instrumentation::IsTracingLayerTreeSnapshots())
     layer->SetDebugInfo(TakeDebugInfo());
 
   layer->SetDoubleSided(double_sided_);
@@ -1320,6 +1316,22 @@
       xform.FlattenTo2d();
     xform.Translate(offset_to_transform_parent().x(),
                     offset_to_transform_parent().y());
+    // A fixed-position layer does not necessarily have the same render target
+    // as its transform node. In particular, its transform node may be an
+    // ancestor of its render target's transform node. For example, given layer
+    // tree R->S->F, suppose F is fixed and S owns a render surface (e.g., say S
+    // has opacity 0.9 and both S and F draw content). Then F's transform node
+    // is the root node, so the target space transform from that node is defined
+    // with respect to the root render surface. But F will render to S's
+    // surface, so must apply a change of basis transform to the target space
+    // transform from its transform node.
+    if (position_constraint_.is_fixed_position()) {
+      gfx::Transform tree_target_to_render_target;
+      tree.ComputeTransform(node->data.content_target_id,
+                            render_target()->transform_tree_index(),
+                            &tree_target_to_render_target);
+      xform.ConcatTransform(tree_target_to_render_target);
+    }
   } else {
     // Surfaces need to apply their sublayer scale.
     xform.Scale(target_node->data.sublayer_scale.x(),
@@ -1354,4 +1366,12 @@
   SetNeedsCommit();
 }
 
+void Layer::DidBeginTracing() {
+  // We'll be dumping layer trees as part of trace, so make sure
+  // PushPropertiesTo() propagates layer debug info to the impl
+  // side -- otherwise this won't happen for the the layers that
+  // remain unchanged since tracing started.
+  SetNeedsPushProperties();
+}
+
 }  // namespace cc
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index e0288c7..4c03e31 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -526,6 +526,8 @@
   // Sets new frame timing requests for this layer.
   void SetFrameTimingRequests(const std::vector<FrameTimingRequest>& requests);
 
+  void DidBeginTracing();
+
  protected:
   friend class LayerImpl;
   friend class TreeSynchronizer;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 0353a78..a1a6161 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -1137,7 +1137,20 @@
 
 gfx::ScrollOffset LayerImpl::PullDeltaForMainThread() {
   RefreshFromScrollDelegate();
-  return scroll_offset_->PullDeltaForMainThread();
+
+  // TODO(miletus): Remove all this temporary flooring machinery when
+  // Blink fully supports fractional scrolls.
+  gfx::ScrollOffset current_offset = CurrentScrollOffset();
+  gfx::Vector2dF current_delta = ScrollDelta();
+  gfx::Vector2dF floored_delta(floor(current_delta.x()),
+                               floor(current_delta.y()));
+  gfx::Vector2dF diff_delta = floored_delta - current_delta;
+  gfx::ScrollOffset tmp_offset = ScrollOffsetWithDelta(current_offset,
+                                                       diff_delta);
+  scroll_offset_->SetCurrent(tmp_offset);
+  gfx::ScrollOffset delta = scroll_offset_->PullDeltaForMainThread();
+  scroll_offset_->SetCurrent(current_offset);
+  return delta;
 }
 
 void LayerImpl::RefreshFromScrollDelegate() {
@@ -1426,7 +1439,8 @@
       parent_->RemoveDependentNeedsPushProperties();
 }
 
-void LayerImpl::GetAllTilesForTracing(std::set<const Tile*>* tiles) const {
+void LayerImpl::GetAllTilesAndPrioritiesForTracing(
+    std::map<const Tile*, TilePriority>* tile_map) const {
 }
 
 void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const {
@@ -1594,4 +1608,20 @@
   return Region(update_rect_);
 }
 
+gfx::Rect LayerImpl::GetEnclosingRectInTargetSpace() const {
+  return MathUtil::MapEnclosingClippedRect(
+      draw_properties_.target_space_transform,
+      gfx::Rect(draw_properties_.content_bounds));
+}
+
+gfx::Rect LayerImpl::GetScaledEnclosingRectInTargetSpace(float scale) const {
+  gfx::Transform scaled_draw_transform =
+      draw_properties_.target_space_transform;
+  scaled_draw_transform.Scale(SK_MScalar1 / scale, SK_MScalar1 / scale);
+  gfx::Size scaled_content_bounds =
+      gfx::ToCeiledSize(gfx::ScaleSize(content_bounds(), scale));
+  return MathUtil::MapEnclosingClippedRect(scaled_draw_transform,
+                                           gfx::Rect(scaled_content_bounds));
+}
+
 }  // namespace cc
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 0357252..e0c42f0 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -5,6 +5,7 @@
 #ifndef CC_LAYERS_LAYER_IMPL_H_
 #define CC_LAYERS_LAYER_IMPL_H_
 
+#include <map>
 #include <set>
 #include <string>
 #include <vector>
@@ -31,6 +32,7 @@
 #include "cc/output/filter_operations.h"
 #include "cc/quads/shared_quad_state.h"
 #include "cc/resources/resource_provider.h"
+#include "cc/resources/tile_priority.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkImageFilter.h"
@@ -561,7 +563,8 @@
   virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl);
   virtual void PushPropertiesTo(LayerImpl* layer);
 
-  virtual void GetAllTilesForTracing(std::set<const Tile*>* tiles) const;
+  virtual void GetAllTilesAndPrioritiesForTracing(
+      std::map<const Tile*, TilePriority>* tile_map) const;
   virtual void AsValueInto(base::trace_event::TracedValue* dict) const;
 
   virtual size_t GPUMemoryUsageInBytes() const;
@@ -600,6 +603,8 @@
   // for layers that provide it.
   virtual Region GetInvalidationRegion();
 
+  virtual gfx::Rect GetEnclosingRectInTargetSpace() const;
+
  protected:
   LayerImpl(LayerTreeImpl* layer_impl,
             int id,
@@ -626,6 +631,8 @@
   // Note carefully this does not affect the current layer.
   void NoteLayerPropertyChangedForDescendants();
 
+  gfx::Rect GetScaledEnclosingRectInTargetSpace(float scale) const;
+
  private:
   void PushScrollOffset(const gfx::ScrollOffset* scroll_offset);
   void DidUpdateScrollOffset();
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
index 0dbf356..1cdbd59 100644
--- a/cc/layers/layer_impl_unittest.cc
+++ b/cc/layers/layer_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "cc/test/fake_output_surface.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/single_thread_proxy.h"
 #include "cc/trees/tree_synchronizer.h"
@@ -87,7 +88,7 @@
   // Create a simple LayerImpl tree:
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   EXPECT_TRUE(host_impl.InitializeRenderer(FakeOutputSurface::Create3d()));
   scoped_ptr<LayerImpl> root_clip =
       LayerImpl::Create(host_impl.active_tree(), 1);
@@ -250,7 +251,7 @@
 TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   EXPECT_TRUE(host_impl.InitializeRenderer(FakeOutputSurface::Create3d()));
   host_impl.active_tree()->SetRootLayer(
       LayerImpl::Create(host_impl.active_tree(), 1));
@@ -368,7 +369,7 @@
 TEST(LayerImplTest, SafeOpaqueBackgroundColor) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   EXPECT_TRUE(host_impl.InitializeRenderer(FakeOutputSurface::Create3d()));
   scoped_ptr<LayerImpl> layer = LayerImpl::Create(host_impl.active_tree(), 1);
 
@@ -399,7 +400,7 @@
 TEST(LayerImplTest, TransformInvertibility) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
 
   scoped_ptr<LayerImpl> layer = LayerImpl::Create(host_impl.active_tree(), 1);
   EXPECT_TRUE(layer->transform().IsInvertible());
@@ -429,7 +430,11 @@
 class LayerImplScrollTest : public testing::Test {
  public:
   LayerImplScrollTest()
-      : host_impl_(settings(), &proxy_, &shared_bitmap_manager_), root_id_(7) {
+      : host_impl_(settings(),
+                   &proxy_,
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_),
+        root_id_(7) {
     host_impl_.active_tree()->SetRootLayer(
         LayerImpl::Create(host_impl_.active_tree(), root_id_));
     host_impl_.active_tree()->root_layer()->AddChild(
@@ -460,6 +465,7 @@
  private:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   int root_id_;
 };
@@ -771,7 +777,10 @@
   };
 
   LayerImplScrollbarSyncTest()
-      : host_impl_(settings(), &proxy_, &shared_bitmap_manager_) {
+      : host_impl_(settings(),
+                   &proxy_,
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_) {
     host_impl_.CreatePendingTree();
 
     CreateLayers(host_impl_.pending_tree());
@@ -836,6 +845,7 @@
  private:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
 };
 
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc
index 0b4c852..72b2e13 100644
--- a/cc/layers/layer_perftest.cc
+++ b/cc/layers/layer_perftest.cc
@@ -10,7 +10,7 @@
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
 #include "cc/test/fake_layer_tree_host_impl.h"
-
+#include "cc/test/test_task_graph_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
 
@@ -30,7 +30,7 @@
 class LayerPerfTest : public testing::Test {
  public:
   LayerPerfTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_),
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
         fake_client_(FakeLayerTreeHostClient::DIRECT_3D),
         timer_(kWarmupRuns,
                base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
@@ -50,6 +50,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
 
   FakeLayerTreeHostClient fake_client_;
diff --git a/cc/layers/layer_position_constraint_unittest.cc b/cc/layers/layer_position_constraint_unittest.cc
index 1c0619b..1eea9c9 100644
--- a/cc/layers/layer_position_constraint_unittest.cc
+++ b/cc/layers/layer_position_constraint_unittest.cc
@@ -11,6 +11,7 @@
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_host_common.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -63,7 +64,8 @@
 
 class LayerPositionConstraintTest : public testing::Test {
  public:
-  LayerPositionConstraintTest() : host_impl_(&proxy_, &shared_bitmap_manager_) {
+  LayerPositionConstraintTest()
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {
     root_ = CreateTreeForTest();
     scroll_ = root_->children()[0];
     fixed_to_top_left_.set_is_fixed_position(true);
@@ -127,6 +129,7 @@
  protected:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   scoped_ptr<LayerImpl> root_;
   LayerImpl* scroll_;
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index 8a61510..10d774c 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -16,6 +16,7 @@
 #include "cc/test/layer_test_common.h"
 #include "cc/test/test_gpu_memory_buffer_manager.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/single_thread_proxy.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -41,7 +42,7 @@
 class MockLayerTreeHost : public LayerTreeHost {
  public:
   explicit MockLayerTreeHost(FakeLayerTreeHostClient* client)
-      : LayerTreeHost(client, nullptr, nullptr, LayerTreeSettings()) {
+      : LayerTreeHost(client, nullptr, nullptr, nullptr, LayerTreeSettings()) {
     InitializeSingleThreaded(client,
                              base::MessageLoopProxy::current(),
                              nullptr);
@@ -60,7 +61,7 @@
 class LayerTest : public testing::Test {
  public:
   LayerTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_),
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
         fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
 
  protected:
@@ -131,6 +132,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
 
   FakeLayerTreeHostClient fake_client_;
@@ -935,24 +937,16 @@
 
   scoped_ptr<LayerTreeHost> Create() {
     return LayerTreeHost::CreateSingleThreaded(
-               &client_,
-               &client_,
-               shared_bitmap_manager_.get(),
-               gpu_memory_buffer_manager_.get(),
-               LayerTreeSettings(),
-               base::MessageLoopProxy::current(),
-               nullptr);
+        &client_, &client_, shared_bitmap_manager_.get(),
+        gpu_memory_buffer_manager_.get(), nullptr, LayerTreeSettings(),
+        base::MessageLoopProxy::current(), nullptr);
   }
 
   scoped_ptr<LayerTreeHost> Create(LayerTreeSettings settings) {
     return LayerTreeHost::CreateSingleThreaded(
-               &client_,
-               &client_,
-               shared_bitmap_manager_.get(),
-               gpu_memory_buffer_manager_.get(),
-               settings,
-               base::MessageLoopProxy::current(),
-               nullptr);
+        &client_, &client_, shared_bitmap_manager_.get(),
+        gpu_memory_buffer_manager_.get(), nullptr, settings,
+        base::MessageLoopProxy::current(), nullptr);
   }
 
  private:
diff --git a/cc/layers/layer_utils_unittest.cc b/cc/layers/layer_utils_unittest.cc
index 5764bc6..534a691 100644
--- a/cc/layers/layer_utils_unittest.cc
+++ b/cc/layers/layer_utils_unittest.cc
@@ -10,6 +10,7 @@
 #include "cc/test/fake_impl_proxy.h"
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/box_f.h"
 #include "ui/gfx/test/gfx_util.h"
@@ -24,7 +25,7 @@
 class LayerUtilsGetAnimationBoundsTest : public testing::Test {
  public:
   LayerUtilsGetAnimationBoundsTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_),
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
         root_(CreateThreeNodeTree(&host_impl_)),
         parent_(root_->children()[0]),
         child_(parent_->children()[0]) {}
@@ -45,6 +46,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   scoped_ptr<LayerImpl> root_;
   LayerImpl* parent_;
diff --git a/cc/layers/painted_scrollbar_layer_impl.cc b/cc/layers/painted_scrollbar_layer_impl.cc
index b05d51e..a085df5 100644
--- a/cc/layers/painted_scrollbar_layer_impl.cc
+++ b/cc/layers/painted_scrollbar_layer_impl.cc
@@ -131,6 +131,10 @@
   }
 }
 
+gfx::Rect PaintedScrollbarLayerImpl::GetEnclosingRectInTargetSpace() const {
+  return GetScaledEnclosingRectInTargetSpace(internal_contents_scale_);
+}
+
 void PaintedScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) {
   if (thumb_thickness_ == thumb_thickness)
     return;
diff --git a/cc/layers/painted_scrollbar_layer_impl.h b/cc/layers/painted_scrollbar_layer_impl.h
index 36a9be9..65d28ae 100644
--- a/cc/layers/painted_scrollbar_layer_impl.h
+++ b/cc/layers/painted_scrollbar_layer_impl.h
@@ -31,6 +31,7 @@
                 ResourceProvider* resource_provider) override;
   void AppendQuads(RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
+  gfx::Rect GetEnclosingRectInTargetSpace() const override;
 
   void SetThumbThickness(int thumb_thickness);
   void SetThumbLength(int thumb_length);
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc
index 3d2d38e..87c4e58 100644
--- a/cc/layers/picture_image_layer_impl_unittest.cc
+++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "cc/test/fake_picture_pile_impl.h"
 #include "cc/test/impl_side_painting_settings.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,7 +38,8 @@
       : proxy_(base::MessageLoopProxy::current()),
         host_impl_(ImplSidePaintingSettings(),
                    &proxy_,
-                   &shared_bitmap_manager_) {
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_) {
     host_impl_.CreatePendingTree();
     host_impl_.InitializeRenderer(FakeOutputSurface::Create3d());
   }
@@ -82,8 +84,9 @@
 
  protected:
   FakeImplProxy proxy_;
-  FakeLayerTreeHostImpl host_impl_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
+  FakeLayerTreeHostImpl host_impl_;
 };
 
 TEST_F(PictureImageLayerImplTest, CalculateContentsScale) {
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 672aab4..ad1d5a0 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -73,6 +73,8 @@
   bool can_use_lcd_text = layer_impl->RasterSourceUsesLCDText();
   scoped_refptr<RasterSource> raster_source =
       recording_source_->CreateRasterSource(can_use_lcd_text);
+  layer_impl->set_gpu_raster_max_texture_size(
+      layer_tree_host()->device_viewport_size());
   layer_impl->UpdateRasterSource(raster_source, &recording_invalidation_,
                                  nullptr);
   DCHECK(recording_invalidation_.IsEmpty());
@@ -95,6 +97,7 @@
   recording_source_->DidMoveToNewCompositor();
   recording_source_->SetSlowdownRasterScaleFactor(
       host->debug_state().slow_down_raster_scale_factor);
+  recording_source_->SetGatherPixelRefs(host->settings().gather_pixel_refs);
 
   DCHECK(host->settings().raster_enabled);
 }
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 93d6581..e1b3724 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -130,6 +130,7 @@
   DCHECK_LE(tilings_->num_tilings(),
             layer_tree_impl()->create_low_res_tiling() ? 2u : 1u);
 
+  layer_impl->set_gpu_raster_max_texture_size(gpu_raster_max_texture_size_);
   layer_impl->UpdateRasterSource(raster_source_, &invalidation_,
                                  tilings_.get());
   DCHECK(invalidation_.IsEmpty());
@@ -666,6 +667,10 @@
   return layer_tree_impl()->RequiresHighResToDraw();
 }
 
+gfx::Rect PictureLayerImpl::GetEnclosingRectInTargetSpace() const {
+  return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale());
+}
+
 gfx::Size PictureLayerImpl::CalculateTileSize(
     const gfx::Size& content_bounds) const {
   int max_texture_size =
@@ -685,8 +690,8 @@
     // For GPU rasterization, we pick an ideal tile size using the viewport
     // so we don't need any settings. The current approach uses 4 tiles
     // to cover the viewport vertically.
-    int viewport_width = layer_tree_impl()->device_viewport_size().width();
-    int viewport_height = layer_tree_impl()->device_viewport_size().height();
+    int viewport_width = gpu_raster_max_texture_size_.width();
+    int viewport_height = gpu_raster_max_texture_size_.height();
     default_tile_width = viewport_width;
     // Also, increase the height proportionally as the width decreases, and
     // pad by our border texels to make the tiles exactly match the viewport.
@@ -1168,11 +1173,11 @@
   *width = DebugColors::TiledContentLayerBorderWidth(layer_tree_impl());
 }
 
-void PictureLayerImpl::GetAllTilesForTracing(
-    std::set<const Tile*>* tiles) const {
+void PictureLayerImpl::GetAllTilesAndPrioritiesForTracing(
+    std::map<const Tile*, TilePriority>* tile_map) const {
   if (!tilings_)
     return;
-  tilings_->GetAllTilesForTracing(tiles);
+  tilings_->GetAllTilesAndPrioritiesForTracing(tile_map);
 }
 
 void PictureLayerImpl::AsValueInto(
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 397cb62..bd13949 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -5,7 +5,7 @@
 #ifndef CC_LAYERS_PICTURE_LAYER_IMPL_H_
 #define CC_LAYERS_PICTURE_LAYER_IMPL_H_
 
-#include <set>
+#include <map>
 #include <string>
 #include <vector>
 
@@ -75,7 +75,11 @@
   TilePriority::PriorityBin GetMaxTilePriorityBin() const override;
   WhichTree GetTree() const override;
   bool RequiresHighResToDraw() const override;
+  gfx::Rect GetEnclosingRectInTargetSpace() const override;
 
+  void set_gpu_raster_max_texture_size(gfx::Size gpu_raster_max_texture_size) {
+    gpu_raster_max_texture_size_ = gpu_raster_max_texture_size;
+  }
   void UpdateRasterSource(scoped_refptr<RasterSource> raster_source,
                           Region* new_invalidation,
                           const PictureLayerTilingSet* pending_set);
@@ -131,7 +135,8 @@
   bool ShouldAdjustRasterScaleDuringScaleAnimations() const;
 
   void GetDebugBorderProperties(SkColor* color, float* width) const override;
-  void GetAllTilesForTracing(std::set<const Tile*>* tiles) const override;
+  void GetAllTilesAndPrioritiesForTracing(
+      std::map<const Tile*, TilePriority>* tile_map) const override;
   void AsValueInto(base::trace_event::TracedValue* dict) const override;
 
   virtual void UpdateIdealScales();
@@ -171,6 +176,8 @@
   gfx::Rect visible_rect_for_tile_priority_;
   gfx::Rect viewport_rect_for_tile_priority_in_content_space_;
 
+  gfx::Size gpu_raster_max_texture_size_;
+
   // List of tilings that were used last time we appended quads. This can be
   // used as an optimization not to remove tilings if they are still being
   // drawn. Note that accessing this vector should only be done in the context
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc
index ea73cb2..47f49be 100644
--- a/cc/layers/picture_layer_impl_perftest.cc
+++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -13,6 +13,7 @@
 #include "cc/test/fake_picture_pile_impl.h"
 #include "cc/test/impl_side_painting_settings.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
@@ -41,7 +42,8 @@
       : proxy_(base::MessageLoopProxy::current()),
         host_impl_(ImplSidePaintingSettings(),
                    &proxy_,
-                   &shared_bitmap_manager_),
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_),
         timer_(kWarmupRuns,
                base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
                kTimeCheckInterval) {}
@@ -170,6 +172,7 @@
 
  protected:
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeImplProxy proxy_;
   FakeLayerTreeHostImpl host_impl_;
   FakePictureLayerImpl* pending_layer_;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 26f3be5..1ed3287 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -27,6 +27,7 @@
 #include "cc/test/impl_side_painting_settings.h"
 #include "cc/test/layer_test_common.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -71,7 +72,10 @@
  public:
   PictureLayerImplTest()
       : proxy_(base::MessageLoopProxy::current()),
-        host_impl_(LowResTilingsSettings(), &proxy_, &shared_bitmap_manager_),
+        host_impl_(LowResTilingsSettings(),
+                   &proxy_,
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_),
         root_id_(6),
         id_(7),
         pending_layer_(nullptr),
@@ -82,7 +86,10 @@
 
   explicit PictureLayerImplTest(const LayerTreeSettings& settings)
       : proxy_(base::MessageLoopProxy::current()),
-        host_impl_(settings, &proxy_, &shared_bitmap_manager_),
+        host_impl_(settings,
+                   &proxy_,
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_),
         root_id_(6),
         id_(7) {
     host_impl_.SetViewportSize(gfx::Size(10000, 10000));
@@ -309,6 +316,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   int root_id_;
   int id_;
@@ -4942,6 +4950,8 @@
   // The +2's below are for border texels.
   host_impl_.SetUseGpuRasterization(true);
   host_impl_.SetViewportSize(gfx::Size(2000, 2000));
+
+  layer->set_gpu_raster_max_texture_size(host_impl_.device_viewport_size());
   result = layer->CalculateTileSize(gfx::Size(10000, 10000));
   EXPECT_EQ(result.width(), 2000);
   EXPECT_EQ(result.height(), 500 + 2);
@@ -4949,6 +4959,7 @@
   // Clamp and round-up, when smaller than viewport.
   // Tile-height doubles to 50% when width shrinks to <= 50%.
   host_impl_.SetViewportSize(gfx::Size(1000, 1000));
+  layer->set_gpu_raster_max_texture_size(host_impl_.device_viewport_size());
   result = layer->CalculateTileSize(gfx::Size(447, 10000));
   EXPECT_EQ(result.width(), 448);
   EXPECT_EQ(result.height(), 500 + 2);
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc
index ab06bb0..48d4abc 100644
--- a/cc/layers/picture_layer_unittest.cc
+++ b/cc/layers/picture_layer_unittest.cc
@@ -62,8 +62,8 @@
     DebugScopedSetImplThread impl_thread(&proxy);
 
     TestSharedBitmapManager shared_bitmap_manager;
-    FakeLayerTreeHostImpl host_impl(
-        ImplSidePaintingSettings(), &proxy, &shared_bitmap_manager);
+    FakeLayerTreeHostImpl host_impl(ImplSidePaintingSettings(), &proxy,
+                                    &shared_bitmap_manager, nullptr);
     host_impl.CreatePendingTree();
     scoped_ptr<FakePictureLayerImpl> layer_impl =
         FakePictureLayerImpl::Create(host_impl.pending_tree(), 1);
@@ -130,12 +130,12 @@
 
   scoped_ptr<LayerTreeHost> host1 = LayerTreeHost::CreateSingleThreaded(
       &host_client1, &host_client1, shared_bitmap_manager.get(), nullptr,
-      settings, base::MessageLoopProxy::current(), nullptr);
+      nullptr, settings, base::MessageLoopProxy::current(), nullptr);
   host_client1.SetLayerTreeHost(host1.get());
 
   scoped_ptr<LayerTreeHost> host2 = LayerTreeHost::CreateSingleThreaded(
       &host_client2, &host_client2, shared_bitmap_manager.get(), nullptr,
-      settings, base::MessageLoopProxy::current(), nullptr);
+      nullptr, settings, base::MessageLoopProxy::current(), nullptr);
   host_client2.SetLayerTreeHost(host2.get());
 
   // The PictureLayer is put in one LayerTreeHost.
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc
index 81b15ad..929407c 100644
--- a/cc/layers/render_surface_unittest.cc
+++ b/cc/layers/render_surface_unittest.cc
@@ -38,7 +38,7 @@
 
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<LayerImpl> owning_layer =
       LayerImpl::Create(host_impl.active_tree(), 1);
   owning_layer->SetHasRenderSurface(true);
@@ -83,7 +83,7 @@
 TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectSharedQuadState) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<LayerImpl> root_layer =
       LayerImpl::Create(host_impl.active_tree(), 1);
 
@@ -147,7 +147,7 @@
 TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectRenderPass) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<LayerImpl> root_layer =
       LayerImpl::Create(host_impl.active_tree(), 1);
 
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 36c37e9..009ec3b 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -21,6 +21,7 @@
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/layer_tree_test.h"
 #include "cc/test/mock_occlusion_tracker.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -516,8 +517,9 @@
  public:
   ScrollbarLayerSolidColorThumbTest() {
     LayerTreeSettings layer_tree_settings;
-    host_impl_.reset(new FakeLayerTreeHostImpl(
-        layer_tree_settings, &proxy_, &shared_bitmap_manager_));
+    host_impl_.reset(new FakeLayerTreeHostImpl(layer_tree_settings, &proxy_,
+                                               &shared_bitmap_manager_,
+                                               &task_graph_runner_));
 
     const int kThumbThickness = 3;
     const int kTrackStart = 0;
@@ -545,6 +547,7 @@
  protected:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
   scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_;
   scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_;
diff --git a/cc/layers/solid_color_layer_impl_unittest.cc b/cc/layers/solid_color_layer_impl_unittest.cc
index d10f35d..f35b82a 100644
--- a/cc/layers/solid_color_layer_impl_unittest.cc
+++ b/cc/layers/solid_color_layer_impl_unittest.cc
@@ -28,7 +28,7 @@
 
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
   layer->draw_properties().visible_content_rect = visible_content_rect;
@@ -54,7 +54,7 @@
 
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
   layer->draw_properties().visible_content_rect = visible_content_rect;
@@ -83,7 +83,7 @@
 
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
   layer->draw_properties().visible_content_rect = visible_content_rect;
@@ -112,7 +112,7 @@
 
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<SolidColorLayerImpl> layer =
       SolidColorLayerImpl::Create(host_impl.active_tree(), 1);
   layer->SetBounds(layer_size);
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 6405db3..b088f6f 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -24,6 +24,7 @@
 #include "cc/test/fake_output_surface.h"
 #include "cc/test/layer_test_common.h"
 #include "cc/test/layer_tree_test.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "cc/trees/blocking_task_runner.h"
 #include "cc/trees/layer_tree_host.h"
@@ -51,7 +52,7 @@
 class MockLayerTreeHost : public LayerTreeHost {
  public:
   explicit MockLayerTreeHost(FakeLayerTreeHostClient* client)
-      : LayerTreeHost(client, nullptr, nullptr, LayerTreeSettings()) {
+      : LayerTreeHost(client, nullptr, nullptr, nullptr, LayerTreeSettings()) {
     InitializeSingleThreaded(client,
                              base::MessageLoopProxy::current(),
                              nullptr);
@@ -172,7 +173,7 @@
   TextureLayerTest()
       : fake_client_(
             FakeLayerTreeHostClient(FakeLayerTreeHostClient::DIRECT_3D)),
-        host_impl_(&proxy_, &shared_bitmap_manager_),
+        host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
         test_data_(&shared_bitmap_manager_) {}
 
  protected:
@@ -195,6 +196,7 @@
   FakeImplProxy proxy_;
   FakeLayerTreeHostClient fake_client_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
   CommonMailboxObjects test_data_;
 };
diff --git a/cc/layers/tiled_layer_impl_unittest.cc b/cc/layers/tiled_layer_impl_unittest.cc
index e98fd09..7a47887 100644
--- a/cc/layers/tiled_layer_impl_unittest.cc
+++ b/cc/layers/tiled_layer_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "cc/test/fake_impl_proxy.h"
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/layer_test_common.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/single_thread_proxy.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,7 +20,8 @@
 
 class TiledLayerImplTest : public testing::Test {
  public:
-  TiledLayerImplTest() : host_impl_(&proxy_, &shared_bitmap_manager_) {}
+  TiledLayerImplTest()
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {}
 
   scoped_ptr<TiledLayerImpl> CreateLayerNoTiles(
       const gfx::Size& tile_size,
@@ -77,6 +79,7 @@
  protected:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
 };
 
diff --git a/cc/layers/tiled_layer_unittest.cc b/cc/layers/tiled_layer_unittest.cc
index 99cc0ca..511df0a 100644
--- a/cc/layers/tiled_layer_unittest.cc
+++ b/cc/layers/tiled_layer_unittest.cc
@@ -95,7 +95,7 @@
     shared_bitmap_manager_.reset(new TestSharedBitmapManager());
     layer_tree_host_ = LayerTreeHost::CreateThreaded(
         &synchonous_output_surface_client_, shared_bitmap_manager_.get(),
-        nullptr, settings_, base::MessageLoopProxy::current(),
+        nullptr, nullptr, settings_, base::MessageLoopProxy::current(),
         impl_thread_.message_loop_proxy(), nullptr);
     synchonous_output_surface_client_.SetLayerTreeHost(layer_tree_host_.get());
     proxy_ = layer_tree_host_->proxy();
@@ -116,8 +116,8 @@
                                                   0,
                                                   false,
                                                   1);
-    host_impl_ = make_scoped_ptr(
-        new FakeLayerTreeHostImpl(proxy_, shared_bitmap_manager_.get()));
+    host_impl_ = make_scoped_ptr(new FakeLayerTreeHostImpl(
+        proxy_, shared_bitmap_manager_.get(), nullptr));
   }
 
   ~TiledLayerTest() override {
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index 0ae4179..16034ea 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -278,27 +278,32 @@
   return !frame->device_clip_rect.Contains(frame->device_viewport_rect);
 }
 
-gfx::Rect DirectRenderer::DeviceClipRectInWindowSpace(const DrawingFrame* frame)
-    const {
+gfx::Rect DirectRenderer::DeviceClipRectInDrawSpace(
+    const DrawingFrame* frame) const {
   gfx::Rect device_clip_rect = frame->device_clip_rect;
-  if (FlippedFramebuffer(frame))
-    device_clip_rect.set_y(current_surface_size_.height() -
-                           device_clip_rect.bottom());
+  device_clip_rect -= current_viewport_rect_.OffsetFromOrigin();
+  device_clip_rect += current_draw_rect_.OffsetFromOrigin();
   return device_clip_rect;
 }
 
-void DirectRenderer::SetScissorStateForQuad(const DrawingFrame* frame,
-                                            const DrawQuad& quad) {
-  if (quad.isClipped()) {
-    SetScissorTestRectInDrawSpace(frame, quad.clipRect());
-    return;
-  }
-  if (NeedDeviceClip(frame)) {
-    SetScissorTestRect(DeviceClipRectInWindowSpace(frame));
-    return;
-  }
+gfx::Rect DirectRenderer::DeviceViewportRectInDrawSpace(
+    const DrawingFrame* frame) const {
+  gfx::Rect device_viewport_rect = frame->device_viewport_rect;
+  device_viewport_rect -= current_viewport_rect_.OffsetFromOrigin();
+  device_viewport_rect += current_draw_rect_.OffsetFromOrigin();
+  return device_viewport_rect;
+}
 
-  EnsureScissorTestDisabled();
+gfx::Rect DirectRenderer::OutputSurfaceRectInDrawSpace(
+    const DrawingFrame* frame) const {
+  if (frame->current_render_pass == frame->root_render_pass) {
+    gfx::Rect output_surface_rect(output_surface_->SurfaceSize());
+    output_surface_rect -= current_viewport_rect_.OffsetFromOrigin();
+    output_surface_rect += current_draw_rect_.OffsetFromOrigin();
+    return output_surface_rect;
+  } else {
+    return frame->current_render_pass->output_rect;
+  }
 }
 
 bool DirectRenderer::ShouldSkipQuad(const DrawQuad& quad,
@@ -315,14 +320,23 @@
   return false;
 }
 
-void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor(
+void DirectRenderer::SetScissorStateForQuad(
     const DrawingFrame* frame,
     const DrawQuad& quad,
-    const gfx::Rect& render_pass_scissor) {
-  gfx::Rect quad_scissor_rect = render_pass_scissor;
-  if (quad.isClipped())
-    quad_scissor_rect.Intersect(quad.clipRect());
-  SetScissorTestRectInDrawSpace(frame, quad_scissor_rect);
+    const gfx::Rect& render_pass_scissor,
+    bool use_render_pass_scissor) {
+  if (use_render_pass_scissor) {
+    gfx::Rect quad_scissor_rect = render_pass_scissor;
+    if (quad.isClipped())
+      quad_scissor_rect.Intersect(quad.clipRect());
+    SetScissorTestRectInDrawSpace(frame, quad_scissor_rect);
+    return;
+  } else if (quad.isClipped()) {
+    SetScissorTestRectInDrawSpace(frame, quad.clipRect());
+    return;
+  }
+
+  EnsureScissorTestDisabled();
 }
 
 void DirectRenderer::SetScissorTestRectInDrawSpace(
@@ -330,8 +344,6 @@
     const gfx::Rect& draw_space_rect) {
   gfx::Rect window_space_rect =
       MoveFromDrawToWindowSpace(frame, draw_space_rect);
-  if (NeedDeviceClip(frame))
-    window_space_rect.Intersect(DeviceClipRectInWindowSpace(frame));
   SetScissorTestRect(window_space_rect);
 }
 
@@ -340,13 +352,9 @@
 void DirectRenderer::DoDrawPolygon(const DrawPolygon& poly,
                                    DrawingFrame* frame,
                                    const gfx::Rect& render_pass_scissor,
-                                   bool using_scissor_as_optimization) {
-  if (using_scissor_as_optimization) {
-    SetScissorStateForQuadWithRenderPassScissor(frame, *poly.original_ref(),
-                                                render_pass_scissor);
-  } else {
-    SetScissorStateForQuad(frame, *poly.original_ref());
-  }
+                                   bool use_render_pass_scissor) {
+  SetScissorStateForQuad(frame, *poly.original_ref(), render_pass_scissor,
+                         use_render_pass_scissor);
 
   // If the poly has not been split, then it is just a normal DrawQuad,
   // and we should save any extra processing that would have to be done.
@@ -365,14 +373,14 @@
 void DirectRenderer::FlushPolygons(ScopedPtrDeque<DrawPolygon>* poly_list,
                                    DrawingFrame* frame,
                                    const gfx::Rect& render_pass_scissor,
-                                   bool using_scissor_as_optimization) {
+                                   bool use_render_pass_scissor) {
   if (poly_list->empty()) {
     return;
   }
 
   BspTree bsp_tree(poly_list);
   BspWalkActionDrawPolygon action_handler(this, frame, render_pass_scissor,
-                                          using_scissor_as_optimization);
+                                          use_render_pass_scissor);
   bsp_tree.TraverseWithActionHandler(&action_handler);
   DCHECK(poly_list->empty());
 }
@@ -383,38 +391,53 @@
   if (!UseRenderPass(frame, render_pass))
     return;
 
-  bool using_scissor_as_optimization = Capabilities().using_partial_swap;
-  gfx::Rect render_pass_scissor;
-  bool draw_rect_covers_full_surface = true;
-  if (frame->current_render_pass == frame->root_render_pass &&
-      !frame->device_viewport_rect.Contains(
-           gfx::Rect(output_surface_->SurfaceSize())))
-    draw_rect_covers_full_surface = false;
+  const gfx::Rect surface_rect_in_draw_space =
+      OutputSurfaceRectInDrawSpace(frame);
+  gfx::Rect render_pass_scissor_in_draw_space = surface_rect_in_draw_space;
 
-  if (using_scissor_as_optimization) {
-    render_pass_scissor = ComputeScissorRectForRenderPass(frame);
-    SetScissorTestRectInDrawSpace(frame, render_pass_scissor);
-    if (!render_pass_scissor.Contains(frame->current_render_pass->output_rect))
-      draw_rect_covers_full_surface = false;
+  if (frame->current_render_pass == frame->root_render_pass) {
+    render_pass_scissor_in_draw_space.Intersect(
+        DeviceViewportRectInDrawSpace(frame));
   }
 
-  if (frame->current_render_pass != frame->root_render_pass ||
-      settings_->should_clear_root_render_pass) {
-    if (NeedDeviceClip(frame)) {
-      SetScissorTestRect(DeviceClipRectInWindowSpace(frame));
-      draw_rect_covers_full_surface = false;
-    } else if (!using_scissor_as_optimization) {
-      EnsureScissorTestDisabled();
-    }
-
-    bool has_external_stencil_test =
-        output_surface_->HasExternalStencilTest() &&
-        frame->current_render_pass == frame->root_render_pass;
-
-    DiscardPixels(has_external_stencil_test, draw_rect_covers_full_surface);
-    ClearFramebuffer(frame, has_external_stencil_test);
+  if (Capabilities().using_partial_swap) {
+    render_pass_scissor_in_draw_space.Intersect(
+        ComputeScissorRectForRenderPass(frame));
   }
 
+  if (NeedDeviceClip(frame)) {
+    render_pass_scissor_in_draw_space.Intersect(
+        DeviceClipRectInDrawSpace(frame));
+  }
+
+  bool render_pass_is_clipped =
+      !render_pass_scissor_in_draw_space.Contains(surface_rect_in_draw_space);
+  bool is_root_render_pass =
+      frame->current_render_pass == frame->root_render_pass;
+  bool has_external_stencil_test =
+      is_root_render_pass && output_surface_->HasExternalStencilTest();
+  bool should_clear_surface =
+      !has_external_stencil_test &&
+      (!is_root_render_pass || settings_->should_clear_root_render_pass);
+
+  // If |has_external_stencil_test| we can't discard or clear. Make sure we
+  // don't need to.
+  DCHECK_IMPLIES(has_external_stencil_test,
+                 !frame->current_render_pass->has_transparent_background);
+
+  SurfaceInitializationMode mode;
+  if (should_clear_surface && render_pass_is_clipped) {
+    mode = SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR;
+  } else if (should_clear_surface) {
+    mode = SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR;
+  } else {
+    mode = SURFACE_INITIALIZATION_MODE_PRESERVE;
+  }
+
+  PrepareSurfaceForPass(
+      frame, mode,
+      MoveFromDrawToWindowSpace(frame, render_pass_scissor_in_draw_space));
+
   const QuadList& quad_list = render_pass->quad_list;
   ScopedPtrDeque<DrawPolygon> poly_list;
 
@@ -425,15 +448,15 @@
     const DrawQuad& quad = **it;
     gfx::QuadF send_quad(quad.visible_rect);
 
-    if (using_scissor_as_optimization &&
-        ShouldSkipQuad(quad, render_pass_scissor)) {
+    if (render_pass_is_clipped &&
+        ShouldSkipQuad(quad, render_pass_scissor_in_draw_space)) {
       continue;
     }
 
     if (last_sorting_context_id != quad.shared_quad_state->sorting_context_id) {
       last_sorting_context_id = quad.shared_quad_state->sorting_context_id;
-      FlushPolygons(&poly_list, frame, render_pass_scissor,
-                    using_scissor_as_optimization);
+      FlushPolygons(&poly_list, frame, render_pass_scissor_in_draw_space,
+                    render_pass_is_clipped);
     }
 
     // This layer is in a 3D sorting context so we add it to the list of
@@ -448,17 +471,13 @@
     }
 
     // We are not in a 3d sorting context, so we should draw the quad normally.
-    if (using_scissor_as_optimization) {
-      SetScissorStateForQuadWithRenderPassScissor(frame, quad,
-                                                  render_pass_scissor);
-    } else {
-      SetScissorStateForQuad(frame, quad);
-    }
+    SetScissorStateForQuad(frame, quad, render_pass_scissor_in_draw_space,
+                           render_pass_is_clipped);
 
     DoDrawQuad(frame, &quad, nullptr);
   }
-  FlushPolygons(&poly_list, frame, render_pass_scissor,
-                using_scissor_as_optimization);
+  FlushPolygons(&poly_list, frame, render_pass_scissor_in_draw_space,
+                render_pass_is_clipped);
   FinishDrawingQuadList();
 }
 
@@ -487,7 +506,14 @@
         size, ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER, RGBA_8888);
   DCHECK(texture->id());
 
-  return BindFramebufferToTexture(frame, texture, render_pass->output_rect);
+  if (BindFramebufferToTexture(frame, texture, render_pass->output_rect)) {
+    InitializeViewport(frame, render_pass->output_rect,
+                       gfx::Rect(render_pass->output_rect.size()),
+                       render_pass->output_rect.size());
+    return true;
+  }
+
+  return false;
 }
 
 bool DirectRenderer::HasAllocatedResourcesForTesting(RenderPassId id) const {
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
index 3399491..dd3d13b 100644
--- a/cc/output/direct_renderer.h
+++ b/cc/output/direct_renderer.h
@@ -63,9 +63,15 @@
   void DoDrawPolygon(const DrawPolygon& poly,
                      DrawingFrame* frame,
                      const gfx::Rect& render_pass_scissor,
-                     bool using_scissor_as_optimization);
+                     bool use_render_pass_scissor);
 
  protected:
+  enum SurfaceInitializationMode {
+    SURFACE_INITIALIZATION_MODE_PRESERVE,
+    SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR,
+    SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR,
+  };
+
   DirectRenderer(RendererClient* client,
                  const RendererSettings* settings,
                  OutputSurface* output_surface,
@@ -83,15 +89,16 @@
                                       const gfx::Rect& draw_rect) const;
 
   bool NeedDeviceClip(const DrawingFrame* frame) const;
-  gfx::Rect DeviceClipRectInWindowSpace(const DrawingFrame* frame) const;
+  gfx::Rect DeviceClipRectInDrawSpace(const DrawingFrame* frame) const;
+  gfx::Rect DeviceViewportRectInDrawSpace(const DrawingFrame* frame) const;
+  gfx::Rect OutputSurfaceRectInDrawSpace(const DrawingFrame* frame) const;
   static gfx::Rect ComputeScissorRectForRenderPass(const DrawingFrame* frame);
-  void SetScissorStateForQuad(const DrawingFrame* frame, const DrawQuad& quad);
+  void SetScissorStateForQuad(const DrawingFrame* frame,
+                              const DrawQuad& quad,
+                              const gfx::Rect& render_pass_scissor,
+                              bool use_render_pass_scissor);
   bool ShouldSkipQuad(const DrawQuad& quad,
                       const gfx::Rect& render_pass_scissor);
-  void SetScissorStateForQuadWithRenderPassScissor(
-      const DrawingFrame* frame,
-      const DrawQuad& quad,
-      const gfx::Rect& render_pass_scissor);
   void SetScissorTestRectInDrawSpace(const DrawingFrame* frame,
                                      const gfx::Rect& draw_space_rect);
 
@@ -100,7 +107,7 @@
   void FlushPolygons(ScopedPtrDeque<DrawPolygon>* poly_list,
                      DrawingFrame* frame,
                      const gfx::Rect& render_pass_scissor,
-                     bool using_scissor_as_optimization);
+                     bool use_render_pass_scissor);
   void DrawRenderPass(DrawingFrame* frame, const RenderPass* render_pass);
   bool UseRenderPass(DrawingFrame* frame, const RenderPass* render_pass);
 
@@ -110,10 +117,10 @@
                                         const gfx::Rect& target_rect) = 0;
   virtual void SetDrawViewport(const gfx::Rect& window_space_viewport) = 0;
   virtual void SetScissorTestRect(const gfx::Rect& scissor_rect) = 0;
-  virtual void DiscardPixels(bool has_external_stencil_test,
-                             bool draw_rect_covers_full_surface) = 0;
-  virtual void ClearFramebuffer(DrawingFrame* frame,
-                                bool has_external_stencil_test) = 0;
+  virtual void PrepareSurfaceForPass(
+      DrawingFrame* frame,
+      SurfaceInitializationMode initialization_mode,
+      const gfx::Rect& render_pass_scissor) = 0;
   // clip_region is a (possibly null) pointer to a quad in the same
   // space as the quad. When non-null only the area of the quad that overlaps
   // with clip_region will be drawn.
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index b49ac8e..e614d8b 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -401,10 +401,8 @@
 
 void GLRenderer::ReleaseRenderPassTextures() { render_pass_textures_.clear(); }
 
-void GLRenderer::DiscardPixels(bool has_external_stencil_test,
-                               bool draw_rect_covers_full_surface) {
-  if (has_external_stencil_test || !draw_rect_covers_full_surface ||
-      !capabilities_.using_discard_framebuffer)
+void GLRenderer::DiscardPixels() {
+  if (!capabilities_.using_discard_framebuffer)
     return;
   bool using_default_framebuffer =
       !current_framebuffer_lock_ &&
@@ -415,15 +413,27 @@
       GL_FRAMEBUFFER, arraysize(attachments), attachments);
 }
 
-void GLRenderer::ClearFramebuffer(DrawingFrame* frame,
-                                  bool has_external_stencil_test) {
-  // It's unsafe to clear when we have a stencil test because glClear ignores
-  // stencil.
-  if (has_external_stencil_test) {
-    DCHECK(!frame->current_render_pass->has_transparent_background);
-    return;
+void GLRenderer::PrepareSurfaceForPass(
+    DrawingFrame* frame,
+    SurfaceInitializationMode initialization_mode,
+    const gfx::Rect& render_pass_scissor) {
+  switch (initialization_mode) {
+    case SURFACE_INITIALIZATION_MODE_PRESERVE:
+      EnsureScissorTestDisabled();
+      return;
+    case SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR:
+      EnsureScissorTestDisabled();
+      DiscardPixels();
+      ClearFramebuffer(frame);
+      break;
+    case SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR:
+      SetScissorTestRect(render_pass_scissor);
+      ClearFramebuffer(frame);
+      break;
   }
+}
 
+void GLRenderer::ClearFramebuffer(DrawingFrame* frame) {
   // On DEBUG builds, opaque render passes are cleared to blue to easily see
   // regions that were not drawn on the screen.
   if (frame->current_render_pass->has_transparent_background)
@@ -2920,9 +2930,6 @@
   DCHECK(gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
              GL_FRAMEBUFFER_COMPLETE ||
          IsContextLost());
-
-  InitializeViewport(
-      frame, target_rect, gfx::Rect(target_rect.size()), target_rect.size());
   return true;
 }
 
diff --git a/cc/output/gl_renderer.cc.rej b/cc/output/gl_renderer.cc.rej
deleted file mode 100644
index 3cf4e17..0000000
--- a/cc/output/gl_renderer.cc.rej
+++ /dev/null
@@ -1,9 +0,0 @@
-diff a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc	(rejected hunks)
-@@ -18,7 +18,6 @@
- #include "build/build_config.h"
- #include "base/trace_event/trace_event.h"
- #include "cc/base/math_util.h"
--#include "cc/layers/video_layer_impl.h"
- #include "cc/output/compositor_frame.h"
- #include "cc/output/compositor_frame_metadata.h"
- #include "cc/output/context_provider.h"
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 27f2c42..ddc8791 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -110,10 +110,9 @@
                                 const gfx::Rect& target_rect) override;
   void SetDrawViewport(const gfx::Rect& window_space_viewport) override;
   void SetScissorTestRect(const gfx::Rect& scissor_rect) override;
-  void DiscardPixels(bool has_external_stencil_test,
-                     bool draw_rect_covers_full_surface) override;
-  void ClearFramebuffer(DrawingFrame* frame,
-                        bool has_external_stencil_test) override;
+  void PrepareSurfaceForPass(DrawingFrame* frame,
+                             SurfaceInitializationMode initialization_mode,
+                             const gfx::Rect& render_pass_scissor) override;
   void DoDrawQuad(DrawingFrame* frame,
                   const class DrawQuad*,
                   const gfx::QuadF* draw_region) override;
@@ -150,6 +149,9 @@
 
   static void ToGLMatrix(float* gl_matrix, const gfx::Transform& transform);
 
+  void DiscardPixels();
+  void ClearFramebuffer(DrawingFrame* frame);
+
   void DrawCheckerboardQuad(const DrawingFrame* frame,
                             const CheckerboardDrawQuad* quad,
                             const gfx::QuadF* clip_region);
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 62bff6f..7e0f8e0 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -97,25 +97,24 @@
 // Explicitly named to be a friend in GLRenderer for shader access.
 class GLRendererShaderPixelTest : public GLRendererPixelTest {
  public:
-  void TestShaders() {
+  void SetUp() override {
+    GLRendererPixelTest::SetUp();
     ASSERT_FALSE(renderer()->IsContextLost());
+  }
+
+  void TearDown() override {
+    GLRendererPixelTest::TearDown();
+    ASSERT_FALSE(renderer()->IsContextLost());
+  }
+
+  void TestBasicShaders() {
     EXPECT_PROGRAM_VALID(renderer()->GetTileCheckerboardProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetDebugBorderProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgramAA());
-    TestShadersWithTexCoordPrecision(TEX_COORD_PRECISION_MEDIUM);
-    TestShadersWithTexCoordPrecision(TEX_COORD_PRECISION_HIGH);
-    ASSERT_FALSE(renderer()->IsContextLost());
   }
 
-  void TestShadersWithTexCoordPrecision(TexCoordPrecision precision) {
-    for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
-      BlendMode blend_mode = static_cast<BlendMode>(i);
-      EXPECT_PROGRAM_VALID(
-          renderer()->GetRenderPassProgram(precision, blend_mode));
-      EXPECT_PROGRAM_VALID(
-          renderer()->GetRenderPassProgramAA(precision, blend_mode));
-    }
+  void TestShadersWithPrecision(TexCoordPrecision precision) {
     EXPECT_PROGRAM_VALID(renderer()->GetTextureProgram(precision));
     EXPECT_PROGRAM_VALID(
         renderer()->GetNonPremultipliedTextureProgram(precision));
@@ -125,20 +124,28 @@
     EXPECT_PROGRAM_VALID(renderer()->GetTextureIOSurfaceProgram(precision));
     EXPECT_PROGRAM_VALID(renderer()->GetVideoYUVProgram(precision));
     EXPECT_PROGRAM_VALID(renderer()->GetVideoYUVAProgram(precision));
-    // This is unlikely to be ever true in tests due to usage of osmesa.
     if (renderer()->Capabilities().using_egl_image)
       EXPECT_PROGRAM_VALID(renderer()->GetVideoStreamTextureProgram(precision));
     else
       EXPECT_FALSE(renderer()->GetVideoStreamTextureProgram(precision));
-    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, SAMPLER_TYPE_EXTERNAL_OES);
   }
 
-  void TestShadersWithSamplerType(TexCoordPrecision precision,
-                                  SamplerType sampler) {
+  void TestShadersWithPrecisionAndBlend(TexCoordPrecision precision,
+                                        BlendMode blend_mode) {
+    EXPECT_PROGRAM_VALID(
+        renderer()->GetRenderPassProgram(precision, blend_mode));
+    EXPECT_PROGRAM_VALID(
+        renderer()->GetRenderPassProgramAA(precision, blend_mode));
+  }
+
+  void TestShadersWithPrecisionAndSampler(TexCoordPrecision precision,
+                                          SamplerType sampler) {
+    if (!renderer()->Capabilities().using_egl_image &&
+        sampler == SAMPLER_TYPE_EXTERNAL_OES) {
+      // This will likely be hit in tests due to usage of osmesa.
+      return;
+    }
+
     EXPECT_PROGRAM_VALID(renderer()->GetTileProgram(precision, sampler));
     EXPECT_PROGRAM_VALID(renderer()->GetTileProgramOpaque(precision, sampler));
     EXPECT_PROGRAM_VALID(renderer()->GetTileProgramAA(precision, sampler));
@@ -147,30 +154,126 @@
         renderer()->GetTileProgramSwizzleOpaque(precision, sampler));
     EXPECT_PROGRAM_VALID(
         renderer()->GetTileProgramSwizzleAA(precision, sampler));
-    for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
-      BlendMode blend_mode = static_cast<BlendMode>(i);
-      for (int l = 0; l <= 1; ++l) {
-        bool mask_for_background = (l == 1);
-        EXPECT_PROGRAM_VALID(
-            renderer()->GetRenderPassMaskProgram(precision,
-                                                 sampler,
-                                                 blend_mode,
-                                                 mask_for_background));
-        EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgramAA(
-            precision, sampler, blend_mode, mask_for_background));
-        EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgramAA(
-            precision, sampler, blend_mode, mask_for_background));
-        EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgram(
-            precision, sampler, blend_mode, mask_for_background));
-      }
+  }
+
+  void TestShadersWithMasks(TexCoordPrecision precision,
+                            SamplerType sampler,
+                            BlendMode blend_mode,
+                            bool mask_for_background) {
+    if (!renderer()->Capabilities().using_egl_image &&
+        sampler == SAMPLER_TYPE_EXTERNAL_OES) {
+      // This will likely be hit in tests due to usage of osmesa.
+      return;
     }
+
+    EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgram(
+        precision, sampler, blend_mode, mask_for_background));
+    EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgramAA(
+        precision, sampler, blend_mode, mask_for_background));
+    EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgramAA(
+        precision, sampler, blend_mode, mask_for_background));
+    EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgram(
+        precision, sampler, blend_mode, mask_for_background));
   }
 };
 
 namespace {
 
 #if !defined(OS_ANDROID) && !defined(OS_WIN)
-TEST_F(GLRendererShaderPixelTest, AllShadersCompile) { TestShaders(); }
+static const TexCoordPrecision kPrecisionList[] = {TEX_COORD_PRECISION_MEDIUM,
+                                                   TEX_COORD_PRECISION_HIGH};
+
+static const BlendMode kBlendModeList[LAST_BLEND_MODE + 1] = {
+    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,
+};
+
+static const SamplerType kSamplerList[] = {
+    SAMPLER_TYPE_2D,
+    SAMPLER_TYPE_2D_RECT,
+    SAMPLER_TYPE_EXTERNAL_OES,
+};
+
+TEST_F(GLRendererShaderPixelTest, BasicShadersCompile) {
+  TestBasicShaders();
+}
+
+class PrecisionShaderPixelTest
+    : public GLRendererShaderPixelTest,
+      public ::testing::WithParamInterface<TexCoordPrecision> {};
+
+TEST_P(PrecisionShaderPixelTest, ShadersCompile) {
+  TestShadersWithPrecision(GetParam());
+}
+
+INSTANTIATE_TEST_CASE_P(PrecisionShadersCompile,
+                        PrecisionShaderPixelTest,
+                        ::testing::ValuesIn(kPrecisionList));
+
+class PrecisionBlendShaderPixelTest
+    : public GLRendererShaderPixelTest,
+      public ::testing::WithParamInterface<
+          std::tr1::tuple<TexCoordPrecision, BlendMode>> {};
+
+TEST_P(PrecisionBlendShaderPixelTest, ShadersCompile) {
+  TestShadersWithPrecisionAndBlend(std::tr1::get<0>(GetParam()),
+                                   std::tr1::get<1>(GetParam()));
+}
+
+INSTANTIATE_TEST_CASE_P(
+    PrecisionBlendShadersCompile,
+    PrecisionBlendShaderPixelTest,
+    ::testing::Combine(::testing::ValuesIn(kPrecisionList),
+                       ::testing::ValuesIn(kBlendModeList)));
+
+class PrecisionSamplerShaderPixelTest
+    : public GLRendererShaderPixelTest,
+      public ::testing::WithParamInterface<
+          std::tr1::tuple<TexCoordPrecision, SamplerType>> {};
+
+TEST_P(PrecisionSamplerShaderPixelTest, ShadersCompile) {
+  TestShadersWithPrecisionAndSampler(std::tr1::get<0>(GetParam()),
+                                     std::tr1::get<1>(GetParam()));
+}
+
+INSTANTIATE_TEST_CASE_P(PrecisionSamplerShadersCompile,
+                        PrecisionSamplerShaderPixelTest,
+                        ::testing::Combine(::testing::ValuesIn(kPrecisionList),
+                                           ::testing::ValuesIn(kSamplerList)));
+
+class MaskShaderPixelTest
+    : public GLRendererShaderPixelTest,
+      public ::testing::WithParamInterface<
+          std::tr1::tuple<TexCoordPrecision, SamplerType, BlendMode, bool>> {};
+
+TEST_P(MaskShaderPixelTest, ShadersCompile) {
+  TestShadersWithMasks(
+      std::tr1::get<0>(GetParam()), std::tr1::get<1>(GetParam()),
+      std::tr1::get<2>(GetParam()), std::tr1::get<3>(GetParam()));
+}
+
+INSTANTIATE_TEST_CASE_P(MaskShadersCompile,
+                        MaskShaderPixelTest,
+                        ::testing::Combine(::testing::ValuesIn(kPrecisionList),
+                                           ::testing::ValuesIn(kSamplerList),
+                                           ::testing::ValuesIn(kBlendModeList),
+                                           ::testing::Bool()));
+
 #endif
 
 class FakeRendererGL : public GLRenderer {
@@ -1309,32 +1412,8 @@
 
 class FlippedScissorAndViewportContext : public TestWebGraphicsContext3D {
  public:
-  FlippedScissorAndViewportContext()
-      : did_call_viewport_(false), did_call_scissor_(false) {}
-  ~FlippedScissorAndViewportContext() override {
-    EXPECT_TRUE(did_call_viewport_);
-    EXPECT_TRUE(did_call_scissor_);
-  }
-
-  void viewport(GLint x, GLint y, GLsizei width, GLsizei height) override {
-    EXPECT_EQ(10, x);
-    EXPECT_EQ(390, y);
-    EXPECT_EQ(100, width);
-    EXPECT_EQ(100, height);
-    did_call_viewport_ = true;
-  }
-
-  void scissor(GLint x, GLint y, GLsizei width, GLsizei height) override {
-    EXPECT_EQ(30, x);
-    EXPECT_EQ(450, y);
-    EXPECT_EQ(20, width);
-    EXPECT_EQ(20, height);
-    did_call_scissor_ = true;
-  }
-
- private:
-  bool did_call_viewport_;
-  bool did_call_scissor_;
+  MOCK_METHOD4(viewport, void(GLint x, GLint y, GLsizei width, GLsizei height));
+  MOCK_METHOD4(scissor, void(GLint x, GLint y, GLsizei width, GLsizei height));
 };
 
 TEST_F(GLRendererTest, ScissorAndViewportWithinNonreshapableSurface) {
@@ -1345,6 +1424,12 @@
   scoped_ptr<FlippedScissorAndViewportContext> context_owned(
       new FlippedScissorAndViewportContext);
 
+  // We expect exactly one call to viewport on this context and exactly two
+  // to scissor (one to scissor the clear, one to scissor the quad draw).
+  EXPECT_CALL(*context_owned, viewport(10, 390, 100, 100));
+  EXPECT_CALL(*context_owned, scissor(10, 390, 100, 100));
+  EXPECT_CALL(*context_owned, scissor(30, 450, 20, 20));
+
   FakeOutputSurfaceClient output_surface_client;
   scoped_ptr<OutputSurface> output_surface(
       new NonReshapableOutputSurface(context_owned.Pass()));
diff --git a/cc/output/render_surface_filters.cc b/cc/output/render_surface_filters.cc
index fc2e88a..7cccb24 100644
--- a/cc/output/render_surface_filters.cc
+++ b/cc/output/render_surface_filters.cc
@@ -206,6 +206,7 @@
             SkIntToScalar(op.amount()),
             SkIntToScalar(op.amount()),
             op.drop_shadow_color(),
+            SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
             image_filter.get()));
         break;
       case FilterOperation::COLOR_MATRIX:
diff --git a/cc/output/renderer_pixeltest.cc.rej b/cc/output/renderer_pixeltest.cc.rej
deleted file mode 100644
index 13d6779..0000000
--- a/cc/output/renderer_pixeltest.cc.rej
+++ /dev/null
@@ -1,245 +0,0 @@
-diff a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc	(rejected hunks)
-@@ -902,243 +901,6 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) {
-       FuzzyPixelOffByOneComparator(true)));
- }
- 
--class VideoGLRendererPixelTest : public GLRendererPixelTest {
-- protected:
--  void CreateEdgeBleedPass(media::VideoFrame::Format format,
--                           RenderPassList* pass_list) {
--    gfx::Rect rect(200, 200);
--
--    RenderPassId id(1, 1);
--    scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--    // Scale the video up so that bilinear filtering kicks in to sample more
--    // than just nearest neighbor would.
--    gfx::Transform scale_by_2;
--    scale_by_2.Scale(2.f, 2.f);
--    gfx::Rect half_rect(100, 100);
--    SharedQuadState* shared_state =
--        CreateTestSharedQuadState(scale_by_2, half_rect, pass.get());
--
--    gfx::Size background_size(200, 200);
--    gfx::Rect green_rect(16, 20, 100, 100);
--    gfx::RectF tex_coord_rect(
--        static_cast<float>(green_rect.x()) / background_size.width(),
--        static_cast<float>(green_rect.y()) / background_size.height(),
--        static_cast<float>(green_rect.width()) / background_size.width(),
--        static_cast<float>(green_rect.height()) / background_size.height());
--
--    // YUV of (149,43,21) should be green (0,255,0) in RGB.
--    // Create a video frame that has a non-green background rect, with a
--    // green sub-rectangle that should be the only thing displayed in
--    // the final image.  Bleeding will appear on all four sides of the video
--    // if the tex coords are not clamped.
--    CreateTestYUVVideoDrawQuad_TwoColor(
--        shared_state, format, false, tex_coord_rect, background_size, 0, 0, 0,
--        green_rect, 149, 43, 21, pass.get(), video_resource_updater_.get(),
--        resource_provider_.get());
--    pass_list->push_back(pass.Pass());
--  }
--
--  void SetUp() override {
--    GLRendererPixelTest::SetUp();
--    video_resource_updater_.reset(new VideoResourceUpdater(
--        output_surface_->context_provider(), resource_provider_.get()));
--  }
--
--  scoped_ptr<VideoResourceUpdater> video_resource_updater_;
--};
--
--TEST_F(VideoGLRendererPixelTest, SimpleYUVRect) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  CreateTestYUVVideoDrawQuad_Striped(shared_state, media::VideoFrame::YV12,
--                                     false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
--                                     pass.get(), video_resource_updater_.get(),
--                                     rect, resource_provider_.get());
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  EXPECT_TRUE(
--      this->RunPixelTest(&pass_list,
--                         base::FilePath(FILE_PATH_LITERAL("yuv_stripes.png")),
--                         FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, OffsetYUVRect) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  // Intentionally sets frame format to I420 for testing coverage.
--  CreateTestYUVVideoDrawQuad_Striped(
--      shared_state, media::VideoFrame::I420, false,
--      gfx::RectF(0.125f, 0.25f, 0.75f, 0.5f), pass.get(),
--      video_resource_updater_.get(), rect, resource_provider_.get());
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  EXPECT_TRUE(this->RunPixelTest(
--      &pass_list,
--      base::FilePath(FILE_PATH_LITERAL("yuv_stripes_offset.png")),
--      FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, SimpleYUVRectBlack) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  // In MPEG color range YUV values of (15,128,128) should produce black.
--  CreateTestYUVVideoDrawQuad_Solid(
--      shared_state, media::VideoFrame::YV12, false,
--      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
--      video_resource_updater_.get(), rect, resource_provider_.get());
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  // If we didn't get black out of the YUV values above, then we probably have a
--  // color range issue.
--  EXPECT_TRUE(this->RunPixelTest(&pass_list,
--                                 base::FilePath(FILE_PATH_LITERAL("black.png")),
--                                 FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, SimpleYUVJRect) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  // YUV of (149,43,21) should be green (0,255,0) in RGB.
--  CreateTestYUVVideoDrawQuad_Solid(
--      shared_state, media::VideoFrame::YV12J, false,
--      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 149, 43, 21, pass.get(),
--      video_resource_updater_.get(), rect, resource_provider_.get());
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  EXPECT_TRUE(this->RunPixelTest(&pass_list,
--                                 base::FilePath(FILE_PATH_LITERAL("green.png")),
--                                 FuzzyPixelOffByOneComparator(true)));
--}
--
--// Test that a YUV video doesn't bleed outside of its tex coords when the
--// tex coord rect is only a partial subrectangle of the coded contents.
--TEST_F(VideoGLRendererPixelTest, YUVEdgeBleed) {
--  RenderPassList pass_list;
--  CreateEdgeBleedPass(media::VideoFrame::YV12J, &pass_list);
--  EXPECT_TRUE(this->RunPixelTest(&pass_list,
--                                 base::FilePath(FILE_PATH_LITERAL("green.png")),
--                                 FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, YUVAEdgeBleed) {
--  RenderPassList pass_list;
--  CreateEdgeBleedPass(media::VideoFrame::YV12A, &pass_list);
--  EXPECT_TRUE(this->RunPixelTest(&pass_list,
--                                 base::FilePath(FILE_PATH_LITERAL("green.png")),
--                                 FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, SimpleYUVJRectGrey) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  // Dark grey in JPEG color range (in MPEG, this is black).
--  CreateTestYUVVideoDrawQuad_Solid(
--      shared_state, media::VideoFrame::YV12J, false,
--      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
--      video_resource_updater_.get(), rect, resource_provider_.get());
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  EXPECT_TRUE(
--      this->RunPixelTest(&pass_list,
--                         base::FilePath(FILE_PATH_LITERAL("dark_grey.png")),
--                         FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, SimpleYUVARect) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  CreateTestYUVVideoDrawQuad_Striped(shared_state, media::VideoFrame::YV12A,
--                                     false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
--                                     pass.get(), video_resource_updater_.get(),
--                                     rect, resource_provider_.get());
--
--  SolidColorDrawQuad* color_quad =
--      pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
--  color_quad->SetNew(shared_state, rect, rect, SK_ColorWHITE, false);
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  EXPECT_TRUE(this->RunPixelTest(
--      &pass_list,
--      base::FilePath(FILE_PATH_LITERAL("yuv_stripes_alpha.png")),
--      FuzzyPixelOffByOneComparator(true)));
--}
--
--TEST_F(VideoGLRendererPixelTest, FullyTransparentYUVARect) {
--  gfx::Rect rect(this->device_viewport_size_);
--
--  RenderPassId id(1, 1);
--  scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
--
--  SharedQuadState* shared_state =
--      CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
--
--  CreateTestYUVVideoDrawQuad_Striped(shared_state, media::VideoFrame::YV12A,
--                                     true, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
--                                     pass.get(), video_resource_updater_.get(),
--                                     rect, resource_provider_.get());
--
--  SolidColorDrawQuad* color_quad =
--      pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
--  color_quad->SetNew(shared_state, rect, rect, SK_ColorBLACK, false);
--
--  RenderPassList pass_list;
--  pass_list.push_back(pass.Pass());
--
--  EXPECT_TRUE(this->RunPixelTest(
--      &pass_list,
--      base::FilePath(FILE_PATH_LITERAL("black.png")),
--      ExactPixelComparator(true)));
--}
--
- TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) {
-   gfx::Rect viewport_rect(this->device_viewport_size_);
- 
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index cecd633..bd99d0b 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -172,10 +172,6 @@
   current_framebuffer_canvas_ =
       skia::AdoptRef(new SkCanvas(current_framebuffer_lock_->sk_bitmap()));
   current_canvas_ = current_framebuffer_canvas_.get();
-  InitializeViewport(frame,
-                     target_rect,
-                     gfx::Rect(target_rect.size()),
-                     target_rect.size());
   return true;
 }
 
@@ -202,11 +198,7 @@
     current_canvas_->clear(color);
 }
 
-void SoftwareRenderer::DiscardPixels(bool has_external_stencil_test,
-                                     bool draw_rect_covers_full_surface) {}
-
-void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame,
-                                        bool has_external_stencil_test) {
+void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame) {
   if (frame->current_render_pass->has_transparent_background) {
     ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
   } else {
@@ -218,6 +210,25 @@
   }
 }
 
+void SoftwareRenderer::PrepareSurfaceForPass(
+    DrawingFrame* frame,
+    SurfaceInitializationMode initialization_mode,
+    const gfx::Rect& render_pass_scissor) {
+  switch (initialization_mode) {
+    case SURFACE_INITIALIZATION_MODE_PRESERVE:
+      EnsureScissorTestDisabled();
+      return;
+    case SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR:
+      EnsureScissorTestDisabled();
+      ClearFramebuffer(frame);
+      break;
+    case SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR:
+      SetScissorTestRect(render_pass_scissor);
+      ClearFramebuffer(frame);
+      break;
+  }
+}
+
 void SoftwareRenderer::SetDrawViewport(
     const gfx::Rect& window_space_viewport) {}
 
diff --git a/cc/output/software_renderer.h b/cc/output/software_renderer.h
index dc8e2fb..ae8beb4 100644
--- a/cc/output/software_renderer.h
+++ b/cc/output/software_renderer.h
@@ -48,10 +48,9 @@
                                 const gfx::Rect& target_rect) override;
   void SetDrawViewport(const gfx::Rect& window_space_viewport) override;
   void SetScissorTestRect(const gfx::Rect& scissor_rect) override;
-  void DiscardPixels(bool has_external_stencil_test,
-                     bool draw_rect_covers_full_surface) override;
-  void ClearFramebuffer(DrawingFrame* frame,
-                        bool has_external_stencil_test) override;
+  void PrepareSurfaceForPass(DrawingFrame* frame,
+                             SurfaceInitializationMode initialization_mode,
+                             const gfx::Rect& render_pass_scissor) override;
 
   void DoDrawQuad(DrawingFrame* frame,
                   const DrawQuad* quad,
@@ -74,6 +73,7 @@
 
  private:
   void ClearCanvas(SkColor color);
+  void ClearFramebuffer(DrawingFrame* frame);
   void SetClipRect(const gfx::Rect& rect);
   bool IsSoftwareResource(ResourceProvider::ResourceId resource_id) const;
 
diff --git a/cc/quads/yuv_video_draw_quad.h.rej b/cc/quads/yuv_video_draw_quad.h.rej
deleted file mode 100644
index a838a4d..0000000
--- a/cc/quads/yuv_video_draw_quad.h.rej
+++ /dev/null
@@ -1,9 +0,0 @@
-diff a/cc/quads/yuv_video_draw_quad.h b/cc/quads/yuv_video_draw_quad.h	(rejected hunks)
-@@ -8,7 +8,6 @@
- #include "base/basictypes.h"
- #include "base/memory/scoped_ptr.h"
- #include "cc/base/cc_export.h"
--#include "cc/layers/video_layer_impl.h"
- #include "cc/quads/draw_quad.h"
- 
- namespace cc {
diff --git a/cc/resources/display_list_raster_source.cc b/cc/resources/display_list_raster_source.cc
index ab0292e..e35900a 100644
--- a/cc/resources/display_list_raster_source.cc
+++ b/cc/resources/display_list_raster_source.cc
@@ -87,13 +87,13 @@
     SkCanvas* canvas,
     const gfx::Rect& canvas_rect,
     float contents_scale) const {
-  RasterCommon(canvas, NULL, canvas_rect, contents_scale, false);
+  RasterCommon(canvas, NULL, canvas_rect, contents_scale);
 }
 
 void DisplayListRasterSource::RasterForAnalysis(skia::AnalysisCanvas* canvas,
                                                 const gfx::Rect& canvas_rect,
                                                 float contents_scale) const {
-  RasterCommon(canvas, canvas, canvas_rect, contents_scale, true);
+  RasterCommon(canvas, canvas, canvas_rect, contents_scale);
 }
 
 void DisplayListRasterSource::PlaybackToCanvas(SkCanvas* canvas,
@@ -103,14 +103,13 @@
       canvas, canvas_rect, gfx::Rect(size_), contents_scale, background_color_,
       clear_canvas_with_debug_color_, requires_clear_);
 
-  RasterCommon(canvas, NULL, canvas_rect, contents_scale, false);
+  RasterCommon(canvas, NULL, canvas_rect, contents_scale);
 }
 
 void DisplayListRasterSource::RasterCommon(SkCanvas* canvas,
                                            SkDrawPictureCallback* callback,
                                            const gfx::Rect& canvas_rect,
-                                           float contents_scale,
-                                           bool is_analysis) const {
+                                           float contents_scale) const {
   canvas->translate(-canvas_rect.x(), -canvas_rect.y());
   gfx::Rect content_rect =
       gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size_), contents_scale));
diff --git a/cc/resources/display_list_raster_source.h b/cc/resources/display_list_raster_source.h
index 44177fb..8c2ab91 100644
--- a/cc/resources/display_list_raster_source.h
+++ b/cc/resources/display_list_raster_source.h
@@ -88,8 +88,7 @@
   void RasterCommon(SkCanvas* canvas,
                     SkDrawPictureCallback* callback,
                     const gfx::Rect& canvas_rect,
-                    float contents_scale,
-                    bool is_analysis) const;
+                    float contents_scale) const;
 
   DISALLOW_COPY_AND_ASSIGN(DisplayListRasterSource);
 };
diff --git a/cc/resources/display_list_recording_source.cc b/cc/resources/display_list_recording_source.cc
index 367b23d..296036d 100644
--- a/cc/resources/display_list_recording_source.cc
+++ b/cc/resources/display_list_recording_source.cc
@@ -28,6 +28,7 @@
 
 DisplayListRecordingSource::DisplayListRecordingSource()
     : slow_down_raster_scale_factor_for_debug_(0),
+      gather_pixel_refs_(false),
       requires_clear_(false),
       is_solid_color_(false),
       solid_color_(SK_ColorTRANSPARENT),
@@ -135,6 +136,10 @@
   slow_down_raster_scale_factor_for_debug_ = factor;
 }
 
+void DisplayListRecordingSource::SetGatherPixelRefs(bool gather_pixel_refs) {
+  gather_pixel_refs_ = gather_pixel_refs;
+}
+
 void DisplayListRecordingSource::SetBackgroundColor(SkColor background_color) {
   background_color_ = background_color;
 }
diff --git a/cc/resources/display_list_recording_source.h b/cc/resources/display_list_recording_source.h
index e827d29..53d7fa5 100644
--- a/cc/resources/display_list_recording_source.h
+++ b/cc/resources/display_list_recording_source.h
@@ -30,6 +30,7 @@
   gfx::Size GetSize() const final;
   void SetEmptyBounds() override;
   void SetSlowdownRasterScaleFactor(int factor) override;
+  void SetGatherPixelRefs(bool gather_pixel_refs) override;
   void SetBackgroundColor(SkColor background_color) override;
   void SetRequiresClear(bool requires_clear) override;
   bool IsSuitableForGpuRasterization() const override;
@@ -42,6 +43,7 @@
   gfx::Rect recorded_viewport_;
   gfx::Size size_;
   int slow_down_raster_scale_factor_for_debug_;
+  bool gather_pixel_refs_;
   bool requires_clear_;
   bool is_solid_color_;
   SkColor solid_color_;
diff --git a/cc/resources/layer_quad.cc b/cc/resources/layer_quad.cc
index ba92314..ff336ba 100644
--- a/cc/resources/layer_quad.cc
+++ b/cc/resources/layer_quad.cc
@@ -22,6 +22,14 @@
   scale(1.0f / tangent.Length());
 }
 
+gfx::PointF LayerQuad::Edge::Intersect(const LayerQuad::Edge& e) const {
+  DCHECK(!degenerate());
+  DCHECK(!e.degenerate());
+
+  return gfx::PointF((y() * e.z() - e.y() * z()) / (x() * e.y() - e.x() * y()),
+                     (x() * e.z() - e.x() * z()) / (e.x() * y() - x() * e.y()));
+}
+
 LayerQuad::LayerQuad(const gfx::QuadF& quad) {
   // Create edges.
   left_ = Edge(quad.p4(), quad.p1());
@@ -46,6 +54,12 @@
       bottom_(bottom) {}
 
 gfx::QuadF LayerQuad::ToQuadF() const {
+  size_t num_degenerate_edges = left_.degenerate() + right_.degenerate() +
+                                top_.degenerate() + bottom_.degenerate();
+  if (num_degenerate_edges > 1) {
+    return gfx::QuadF();
+  }
+
   if (left_.degenerate()) {
     return gfx::QuadF(top_.Intersect(bottom_), top_.Intersect(right_),
                       right_.Intersect(bottom_), bottom_.Intersect(top_));
diff --git a/cc/resources/layer_quad.h b/cc/resources/layer_quad.h
index 7433645..328f666 100644
--- a/cc/resources/layer_quad.h
+++ b/cc/resources/layer_quad.h
@@ -20,7 +20,7 @@
 
 class CC_EXPORT LayerQuad {
  public:
-  class Edge {
+  class CC_EXPORT Edge {
    public:
     Edge() : x_(0), y_(0), z_(0), degenerate_(false) {}
     Edge(const gfx::PointF& p, const gfx::PointF& q);
@@ -59,11 +59,7 @@
 
     bool degenerate() const { return degenerate_; }
 
-    gfx::PointF Intersect(const Edge& e) const {
-      return gfx::PointF(
-          (y() * e.z() - e.y() * z()) / (x() * e.y() - e.x() * y()),
-          (x() * e.z() - e.x() * z()) / (e.x() * y() - x() * e.y()));
-    }
+    gfx::PointF Intersect(const Edge& e) const;
 
    private:
     float x_;
diff --git a/cc/resources/layer_quad_unittest.cc b/cc/resources/layer_quad_unittest.cc
index 3560b8d..90f61be 100644
--- a/cc/resources/layer_quad_unittest.cc
+++ b/cc/resources/layer_quad_unittest.cc
@@ -38,5 +38,32 @@
   EXPECT_EQ(layer_quad.ToQuadF(), quad);
 }
 
+TEST(LayerQuadTest, Degenerate) {
+  gfx::QuadF quad;
+  gfx::PointF p1(1.0f, 1.0f);
+  gfx::PointF p2(0.0f, 1.0f);
+  gfx::PointF p3(1.0f, 0.0f);
+  gfx::QuadF triangle(p1, p2, p3, p1);
+
+  LayerQuad::Edge e1d(p1, p1);
+  LayerQuad::Edge e2d(p2, p2);
+  LayerQuad::Edge e2(p1, p2);
+  LayerQuad::Edge e3(p2, p3);
+  LayerQuad::Edge e4(p3, p1);
+  EXPECT_TRUE(e1d.degenerate());
+  EXPECT_TRUE(e2d.degenerate());
+  EXPECT_FALSE(e2.degenerate());
+  EXPECT_FALSE(e3.degenerate());
+  EXPECT_FALSE(e4.degenerate());
+
+  LayerQuad degenerate_quad(e1d, e2d, e2, e3);
+  // With more than one degenerate edge, we expect the quad to be zero.
+  EXPECT_EQ(quad, degenerate_quad.ToQuadF());
+
+  LayerQuad triangle_quad(e1d, e2, e3, e4);
+  // With only one degenerate edge, we expect the quad to be a triangle.
+  EXPECT_EQ(triangle, triangle_quad.ToQuadF());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/resources/picture.cc b/cc/resources/picture.cc
index 867a86a..e88ff21 100644
--- a/cc/resources/picture.cc
+++ b/cc/resources/picture.cc
@@ -213,11 +213,7 @@
   canvas->translate(SkFloatToScalar(-layer_rect_.x()),
                     SkFloatToScalar(-layer_rect_.y()));
 
-  SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
-                                         layer_rect_.y(),
-                                         layer_rect_.width(),
-                                         layer_rect_.height());
-  canvas->clipRect(layer_skrect);
+  canvas->clipRect(gfx::RectToSkRect(layer_rect_));
 
   painter->PaintContents(canvas.get(), layer_rect_, painting_control);
 
diff --git a/cc/resources/picture_layer_tiling.cc b/cc/resources/picture_layer_tiling.cc
index 1e7281d..171a8cb 100644
--- a/cc/resources/picture_layer_tiling.cc
+++ b/cc/resources/picture_layer_tiling.cc
@@ -57,7 +57,6 @@
       raster_source_(raster_source),
       resolution_(NON_IDEAL_RESOLUTION),
       tiling_data_(gfx::Size(), gfx::Size(), kBorderTexels),
-      last_impl_frame_time_in_seconds_(0.0),
       can_require_tiles_for_activation_(false),
       current_content_to_screen_scale_(0.f),
       has_visible_rect_tiles_(false),
@@ -530,21 +529,26 @@
     double current_frame_time_in_seconds,
     const gfx::Rect& visible_rect_in_content_space) const {
   gfx::Rect skewport = visible_rect_in_content_space;
-  if (last_impl_frame_time_in_seconds_ == 0.0)
+  if (skewport.IsEmpty())
     return skewport;
 
-  double time_delta =
-      current_frame_time_in_seconds - last_impl_frame_time_in_seconds_;
+  if (visible_rect_history_[1].frame_time_in_seconds == 0.0)
+    return skewport;
+
+  double time_delta = current_frame_time_in_seconds -
+                      visible_rect_history_[1].frame_time_in_seconds;
   if (time_delta == 0.0)
     return skewport;
 
   double extrapolation_multiplier =
       skewport_target_time_in_seconds_ / time_delta;
 
-  int old_x = last_visible_rect_in_content_space_.x();
-  int old_y = last_visible_rect_in_content_space_.y();
-  int old_right = last_visible_rect_in_content_space_.right();
-  int old_bottom = last_visible_rect_in_content_space_.bottom();
+  int old_x = visible_rect_history_[1].visible_rect_in_content_space.x();
+  int old_y = visible_rect_history_[1].visible_rect_in_content_space.y();
+  int old_right =
+      visible_rect_history_[1].visible_rect_in_content_space.right();
+  int old_bottom =
+      visible_rect_history_[1].visible_rect_in_content_space.bottom();
 
   int new_x = visible_rect_in_content_space.x();
   int new_y = visible_rect_in_content_space.y();
@@ -563,11 +567,13 @@
                  extrapolation_multiplier * (old_right - new_right),
                  extrapolation_multiplier * (old_bottom - new_bottom));
 
-  // Clip the skewport to |max_skewport|.
+  // Ensure that visible rect is contained in the skewport.
+  skewport.Union(visible_rect_in_content_space);
+
+  // Clip the skewport to |max_skewport|. This needs to happen after the
+  // union in case intersecting would have left the empty rect.
   skewport.Intersect(max_skewport);
 
-  // Finally, ensure that visible rect is contained in the skewport.
-  skewport.Union(visible_rect_in_content_space);
   return skewport;
 }
 
@@ -587,9 +593,9 @@
       gfx::ScaleToEnclosingRect(viewport_in_layer_space, contents_scale_);
 
   if (tiling_size().IsEmpty()) {
-    last_impl_frame_time_in_seconds_ = current_frame_time_in_seconds;
+    UpdateVisibleRectHistory(current_frame_time_in_seconds,
+                             visible_rect_in_content_space);
     last_viewport_in_layer_space_ = viewport_in_layer_space;
-    last_visible_rect_in_content_space_ = visible_rect_in_content_space;
     return false;
   }
 
@@ -621,9 +627,9 @@
                                              content_to_screen_scale);
   soon_border_rect.Inset(-border, -border, -border, -border);
 
-  last_impl_frame_time_in_seconds_ = current_frame_time_in_seconds;
+  UpdateVisibleRectHistory(current_frame_time_in_seconds,
+                           visible_rect_in_content_space);
   last_viewport_in_layer_space_ = viewport_in_layer_space;
-  last_visible_rect_in_content_space_ = visible_rect_in_content_space;
 
   SetLiveTilesRect(eventually_rect);
   UpdateTilePriorityRects(
@@ -806,7 +812,8 @@
   WhichTree tree = client_->GetTree();
   WhichTree twin_tree = tree == ACTIVE_TREE ? PENDING_TREE : ACTIVE_TREE;
 
-  UpdateTilePriorityForTree(tile, tree);
+  tile->SetPriority(tree, ComputePriorityForTile(tile));
+  UpdateRequiredStateForTile(tile, tree);
 
   const PictureLayerTiling* twin_tiling =
       client_->GetPendingOrActiveTwinTiling(this);
@@ -820,24 +827,13 @@
     return;
   }
 
-  twin_tiling->UpdateTilePriorityForTree(tile, twin_tree);
+  tile->SetPriority(twin_tree, twin_tiling->ComputePriorityForTile(tile));
+  twin_tiling->UpdateRequiredStateForTile(tile, twin_tree);
 }
 
-void PictureLayerTiling::UpdateTilePriorityForTree(Tile* tile,
-                                                   WhichTree tree) const {
-  // TODO(vmpstr): This code should return the priority instead of setting it on
-  // the tile. This should be a part of the change to move tile priority from
-  // tiles into iterators.
-  TilePriority::PriorityBin max_tile_priority_bin =
-      client_->GetMaxTilePriorityBin();
-
-  DCHECK_EQ(TileAt(tile->tiling_i_index(), tile->tiling_j_index()), tile);
-  gfx::Rect tile_bounds =
-      tiling_data_.TileBounds(tile->tiling_i_index(), tile->tiling_j_index());
-
-  if (max_tile_priority_bin <= TilePriority::NOW &&
-      current_visible_rect_.Intersects(tile_bounds)) {
-    tile->SetPriority(tree, TilePriority(resolution_, TilePriority::NOW, 0));
+void PictureLayerTiling::UpdateRequiredStateForTile(Tile* tile,
+                                                    WhichTree tree) const {
+  if (tile->priority(tree).priority_bin == TilePriority::NOW) {
     if (tree == PENDING_TREE) {
       tile->set_required_for_activation(
           IsTileRequiredForActivationIfVisible(tile));
@@ -848,11 +844,28 @@
     return;
   }
 
+  // Non-NOW bin tiles are not required or occluded.
   if (tree == PENDING_TREE)
     tile->set_required_for_activation(false);
   else
     tile->set_required_for_draw(false);
   tile->set_is_occluded(tree, false);
+}
+
+TilePriority PictureLayerTiling::ComputePriorityForTile(
+    const Tile* tile) const {
+  // TODO(vmpstr): See if this can be moved to iterators.
+  TilePriority::PriorityBin max_tile_priority_bin =
+      client_->GetMaxTilePriorityBin();
+
+  DCHECK_EQ(TileAt(tile->tiling_i_index(), tile->tiling_j_index()), tile);
+  gfx::Rect tile_bounds =
+      tiling_data_.TileBounds(tile->tiling_i_index(), tile->tiling_j_index());
+
+  if (max_tile_priority_bin <= TilePriority::NOW &&
+      current_visible_rect_.Intersects(tile_bounds)) {
+    return TilePriority(resolution_, TilePriority::NOW, 0);
+  }
 
   DCHECK_GT(current_content_to_screen_scale_, 0.f);
   float distance_to_visible =
@@ -862,27 +875,38 @@
   if (max_tile_priority_bin <= TilePriority::SOON &&
       (current_soon_border_rect_.Intersects(tile_bounds) ||
        current_skewport_rect_.Intersects(tile_bounds))) {
-    tile->SetPriority(
-        tree,
-        TilePriority(resolution_, TilePriority::SOON, distance_to_visible));
-    return;
+    return TilePriority(resolution_, TilePriority::SOON, distance_to_visible);
   }
 
-  tile->SetPriority(
-      tree,
-      TilePriority(resolution_, TilePriority::EVENTUALLY, distance_to_visible));
+  return TilePriority(resolution_, TilePriority::EVENTUALLY,
+                      distance_to_visible);
 }
 
-void PictureLayerTiling::GetAllTilesForTracing(
-    std::set<const Tile*>* tiles) const {
-  for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it)
-    tiles->insert(it->second.get());
+void PictureLayerTiling::GetAllTilesAndPrioritiesForTracing(
+    std::map<const Tile*, TilePriority>* tile_map) const {
+  const PictureLayerTiling* twin_tiling =
+      client_->GetPendingOrActiveTwinTiling(this);
+  for (const auto& tile_pair : tiles_) {
+    const Tile* tile = tile_pair.second.get();
+    const TilePriority& priority = ComputePriorityForTile(tile);
+    const TilePriority& twin_priority =
+        twin_tiling ? twin_tiling->ComputePriorityForTile(tile)
+                    : TilePriority();
+
+    // Store combined priority.
+    (*tile_map)[tile] = TilePriority(priority, twin_priority);
+  }
 }
 
 void PictureLayerTiling::AsValueInto(
     base::trace_event::TracedValue* state) const {
   state->SetInteger("num_tiles", tiles_.size());
   state->SetDouble("content_scale", contents_scale_);
+  MathUtil::AddToTracedValue("visible_rect", current_visible_rect_, state);
+  MathUtil::AddToTracedValue("skewport_rect", current_skewport_rect_, state);
+  MathUtil::AddToTracedValue("soon_rect", current_soon_border_rect_, state);
+  MathUtil::AddToTracedValue("eventually_rect", current_eventually_rect_,
+                             state);
   MathUtil::AddToTracedValue("tiling_size", tiling_size(), state);
 }
 
diff --git a/cc/resources/picture_layer_tiling.h b/cc/resources/picture_layer_tiling.h
index 65e7ee0..ea258a3 100644
--- a/cc/resources/picture_layer_tiling.h
+++ b/cc/resources/picture_layer_tiling.h
@@ -5,7 +5,7 @@
 #ifndef CC_RESOURCES_PICTURE_LAYER_TILING_H_
 #define CC_RESOURCES_PICTURE_LAYER_TILING_H_
 
-#include <set>
+#include <map>
 #include <utility>
 #include <vector>
 
@@ -140,6 +140,8 @@
   bool IsTileRequiredForDrawIfVisible(const Tile* tile) const;
 
   void UpdateTileAndTwinPriority(Tile* tile) const;
+  TilePriority ComputePriorityForTile(const Tile* tile) const;
+  void UpdateRequiredStateForTile(Tile* tile, WhichTree tree) const;
   bool has_visible_rect_tiles() const { return has_visible_rect_tiles_; }
   bool has_skewport_rect_tiles() const { return has_skewport_rect_tiles_; }
   bool has_soon_border_rect_tiles() const {
@@ -211,7 +213,8 @@
                                 double current_frame_time_in_seconds,
                                 const Occlusion& occlusion_in_layer_space);
 
-  void GetAllTilesForTracing(std::set<const Tile*>* tiles) const;
+  void GetAllTilesAndPrioritiesForTracing(
+      std::map<const Tile*, TilePriority>* tile_map) const;
   void AsValueInto(base::trace_event::TracedValue* array) const;
   size_t GPUMemoryUsageInBytes() const;
 
@@ -232,7 +235,7 @@
       RectExpansionCache* cache);
 
   bool has_ever_been_updated() const {
-    return last_impl_frame_time_in_seconds_ != 0.0;
+    return visible_rect_history_[0].frame_time_in_seconds != 0.0;
   }
 
  protected:
@@ -244,6 +247,11 @@
   typedef std::pair<int, int> TileMapKey;
   typedef base::hash_map<TileMapKey, scoped_refptr<Tile>> TileMap;
 
+  struct FrameVisibleRect {
+    gfx::Rect visible_rect_in_content_space;
+    double frame_time_in_seconds = 0.0;
+  };
+
   PictureLayerTiling(float contents_scale,
                      scoped_refptr<RasterSource> raster_source,
                      PictureLayerTilingClient* client,
@@ -279,9 +287,21 @@
   bool NeedsUpdateForFrameAtTimeAndViewport(
       double frame_time_in_seconds,
       const gfx::Rect& viewport_in_layer_space) {
-    return frame_time_in_seconds != last_impl_frame_time_in_seconds_ ||
+    return frame_time_in_seconds !=
+               visible_rect_history_[0].frame_time_in_seconds ||
            viewport_in_layer_space != last_viewport_in_layer_space_;
   }
+  void UpdateVisibleRectHistory(
+      double frame_time_in_seconds,
+      const gfx::Rect& visible_rect_in_content_space) {
+    visible_rect_history_[1] = visible_rect_history_[0];
+    visible_rect_history_[0].frame_time_in_seconds = frame_time_in_seconds;
+    visible_rect_history_[0].visible_rect_in_content_space =
+        visible_rect_in_content_space;
+    // If we don't have a second history item, set it to the most recent one.
+    if (visible_rect_history_[1].frame_time_in_seconds == 0.0)
+      visible_rect_history_[1] = visible_rect_history_[0];
+  }
 
   const size_t max_tiles_for_interest_area_;
   const float skewport_target_time_in_seconds_;
@@ -298,10 +318,9 @@
   TileMap tiles_;  // It is not legal to have a NULL tile in the tiles_ map.
   gfx::Rect live_tiles_rect_;
 
-  // State saved for computing velocities based upon finite differences.
-  double last_impl_frame_time_in_seconds_;
   gfx::Rect last_viewport_in_layer_space_;
-  gfx::Rect last_visible_rect_in_content_space_;
+  // State saved for computing velocities based upon finite differences.
+  FrameVisibleRect visible_rect_history_[2];
 
   bool can_require_tiles_for_activation_;
 
diff --git a/cc/resources/picture_layer_tiling_set.cc b/cc/resources/picture_layer_tiling_set.cc
index bd150f4..e616ce2 100644
--- a/cc/resources/picture_layer_tiling_set.cc
+++ b/cc/resources/picture_layer_tiling_set.cc
@@ -328,10 +328,10 @@
   return updated;
 }
 
-void PictureLayerTilingSet::GetAllTilesForTracing(
-    std::set<const Tile*>* tiles) const {
+void PictureLayerTilingSet::GetAllTilesAndPrioritiesForTracing(
+    std::map<const Tile*, TilePriority>* tile_map) const {
   for (auto* tiling : tilings_)
-    tiling->GetAllTilesForTracing(tiles);
+    tiling->GetAllTilesAndPrioritiesForTracing(tile_map);
 }
 
 PictureLayerTilingSet::CoverageIterator::CoverageIterator(
diff --git a/cc/resources/picture_layer_tiling_set.h b/cc/resources/picture_layer_tiling_set.h
index a4c2fd8..24da44d 100644
--- a/cc/resources/picture_layer_tiling_set.h
+++ b/cc/resources/picture_layer_tiling_set.h
@@ -112,7 +112,8 @@
                             const Occlusion& occlusion_in_layer_space,
                             bool can_require_tiles_for_activation);
 
-  void GetAllTilesForTracing(std::set<const Tile*>* tiles) const;
+  void GetAllTilesAndPrioritiesForTracing(
+      std::map<const Tile*, TilePriority>* tile_map) const;
 
   // For a given rect, iterates through tiles that can fill it.  If no
   // set of tiles with resources can fill the rect, then it will iterate
diff --git a/cc/resources/picture_layer_tiling_unittest.cc b/cc/resources/picture_layer_tiling_unittest.cc
index d4a8778..5999ecd 100644
--- a/cc/resources/picture_layer_tiling_unittest.cc
+++ b/cc/resources/picture_layer_tiling_unittest.cc
@@ -591,7 +591,7 @@
   EXPECT_EQ(350, expand_skewport.height());
   EXPECT_TRUE(expand_skewport.Contains(gfx::Rect(-50, -50, 200, 200)));
 
-  // Expand the viewport past the limit.
+  // Expand the viewport past the limit in all directions.
   gfx::Rect big_expand_skewport =
       tiling->ComputeSkewport(1.5, gfx::Rect(-500, -500, 1500, 1500));
 
@@ -600,6 +600,23 @@
   EXPECT_EQ(1650, big_expand_skewport.width());
   EXPECT_EQ(1650, big_expand_skewport.height());
   EXPECT_TRUE(big_expand_skewport.Contains(gfx::Rect(-500, -500, 1500, 1500)));
+
+  // Shrink the skewport in all directions.
+  gfx::Rect shrink_viewport =
+      tiling->ComputeSkewport(1.5, gfx::Rect(0, 0, 100, 100));
+  EXPECT_EQ(0, shrink_viewport.x());
+  EXPECT_EQ(0, shrink_viewport.y());
+  EXPECT_EQ(100, shrink_viewport.width());
+  EXPECT_EQ(100, shrink_viewport.height());
+
+  // Move the skewport really far in one direction.
+  gfx::Rect move_skewport_far =
+      tiling->ComputeSkewport(1.5, gfx::Rect(0, 5000, 100, 100));
+  EXPECT_EQ(0, move_skewport_far.x());
+  EXPECT_EQ(5000, move_skewport_far.y());
+  EXPECT_EQ(100, move_skewport_far.width());
+  EXPECT_EQ(175, move_skewport_far.height());
+  EXPECT_TRUE(move_skewport_far.Contains(gfx::Rect(0, 5000, 100, 100)));
 }
 
 TEST(PictureLayerTilingTest, ComputeSkewport) {
@@ -665,6 +682,75 @@
   EXPECT_EQ(160, expanded_skewport.height());
 }
 
+TEST(PictureLayerTilingTest, SkewportThroughUpdateTilePriorities) {
+  FakePictureLayerTilingClient client;
+
+  gfx::Rect viewport(0, 0, 100, 100);
+  gfx::Size layer_bounds(200, 200);
+
+  client.SetTileSize(gfx::Size(100, 100));
+  client.set_tree(ACTIVE_TREE);
+
+  scoped_refptr<FakePicturePileImpl> pile =
+      FakePicturePileImpl::CreateFilledPileWithDefaultTileSize(layer_bounds);
+  scoped_ptr<TestablePictureLayerTiling> tiling =
+      TestablePictureLayerTiling::Create(1.0f, pile, &client,
+                                         LayerTreeSettings());
+
+  tiling->ComputeTilePriorityRects(viewport, 1.f, 1.0, Occlusion());
+
+  // Move viewport down 50 pixels in 0.5 seconds.
+  gfx::Rect viewport_50 = gfx::Rect(0, 50, 100, 100);
+  gfx::Rect skewport_50 = tiling->ComputeSkewport(1.5, viewport_50);
+
+  EXPECT_EQ(gfx::Rect(0, 50, 100, 200), skewport_50);
+  tiling->ComputeTilePriorityRects(viewport_50, 1.f, 1.5, Occlusion());
+
+  gfx::Rect viewport_100 = gfx::Rect(0, 100, 100, 100);
+  gfx::Rect skewport_100 = tiling->ComputeSkewport(2.0, viewport_100);
+
+  EXPECT_EQ(gfx::Rect(0, 100, 100, 200), skewport_100);
+  tiling->ComputeTilePriorityRects(viewport_100, 1.f, 2.0, Occlusion());
+
+  // Advance time, but not the viewport.
+  gfx::Rect result = tiling->ComputeSkewport(2.5, viewport_100);
+  // Since the history did advance, we should still get a skewport but a smaller
+  // one.
+  EXPECT_EQ(gfx::Rect(0, 100, 100, 150), result);
+  tiling->ComputeTilePriorityRects(viewport_100, 1.f, 2.5, Occlusion());
+
+  // Advance time again.
+  result = tiling->ComputeSkewport(3.0, viewport_100);
+  EXPECT_EQ(viewport_100, result);
+  tiling->ComputeTilePriorityRects(viewport_100, 1.f, 3.0, Occlusion());
+
+  // Ensure we have a skewport.
+  gfx::Rect viewport_150 = gfx::Rect(0, 150, 100, 100);
+  gfx::Rect skewport_150 = tiling->ComputeSkewport(3.5, viewport_150);
+  EXPECT_EQ(gfx::Rect(0, 150, 100, 150), skewport_150);
+  tiling->ComputeTilePriorityRects(viewport_150, 1.f, 3.5, Occlusion());
+
+  // Advance the viewport, but not the time.
+  gfx::Rect viewport_200 = gfx::Rect(0, 200, 100, 100);
+  gfx::Rect skewport_200 = tiling->ComputeSkewport(3.5, viewport_200);
+  EXPECT_EQ(gfx::Rect(0, 200, 100, 300), skewport_200);
+
+  // Ensure that continued calls with the same value, produce the same skewport.
+  tiling->ComputeTilePriorityRects(viewport_150, 1.f, 3.5, Occlusion());
+  EXPECT_EQ(gfx::Rect(0, 200, 100, 300), skewport_200);
+  tiling->ComputeTilePriorityRects(viewport_150, 1.f, 3.5, Occlusion());
+  EXPECT_EQ(gfx::Rect(0, 200, 100, 300), skewport_200);
+
+  tiling->ComputeTilePriorityRects(viewport_200, 1.f, 3.5, Occlusion());
+
+  // This should never happen, but advance the viewport yet again keeping the
+  // time the same.
+  gfx::Rect viewport_250 = gfx::Rect(0, 250, 100, 100);
+  gfx::Rect skewport_250 = tiling->ComputeSkewport(3.5, viewport_250);
+  EXPECT_EQ(viewport_250, skewport_250);
+  tiling->ComputeTilePriorityRects(viewport_250, 1.f, 3.5, Occlusion());
+}
+
 TEST(PictureLayerTilingTest, ViewportDistanceWithScale) {
   FakePictureLayerTilingClient client;
 
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index 04e0801..b5b9c79 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -10,7 +10,6 @@
 
 #include "cc/base/region.h"
 #include "cc/resources/picture_pile_impl.h"
-#include "cc/resources/tile_task_worker_pool.h"
 #include "skia/ext/analysis_canvas.h"
 
 namespace {
@@ -166,6 +165,7 @@
                          const gfx::Size& tile_grid_size)
     : min_contents_scale_(0),
       slow_down_raster_scale_factor_for_debug_(0),
+      gather_pixel_refs_(false),
       has_any_recordings_(false),
       clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
       requires_clear_(true),
@@ -539,15 +539,9 @@
     int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
     scoped_refptr<Picture> picture;
 
-    // Note: Currently, gathering of pixel refs when using a single
-    // raster thread doesn't provide any benefit. This might change
-    // in the future but we avoid it for now to reduce the cost of
-    // Picture::Create.
-    bool gather_pixel_refs = TileTaskWorkerPool::GetNumWorkerThreads() > 1;
-
     for (int i = 0; i < repeat_count; i++) {
       picture = Picture::Create(padded_record_rect, painter, tile_grid_size_,
-                                gather_pixel_refs, recording_mode);
+                                gather_pixel_refs_, recording_mode);
       // Note the '&&' with previous is-suitable state.
       // This means that once a picture-pile becomes unsuitable for gpu
       // rasterization due to some content, it will continue to be unsuitable
@@ -623,6 +617,10 @@
   slow_down_raster_scale_factor_for_debug_ = factor;
 }
 
+void PicturePile::SetGatherPixelRefs(bool gather_pixel_refs) {
+  gather_pixel_refs_ = gather_pixel_refs;
+}
+
 void PicturePile::SetBackgroundColor(SkColor background_color) {
   background_color_ = background_color;
 }
diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h
index bdba579..f3c9569 100644
--- a/cc/resources/picture_pile.h
+++ b/cc/resources/picture_pile.h
@@ -35,6 +35,7 @@
   gfx::Size GetSize() const final;
   void SetEmptyBounds() override;
   void SetSlowdownRasterScaleFactor(int factor) override;
+  void SetGatherPixelRefs(bool gather_pixel_refs) override;
   void SetBackgroundColor(SkColor background_color) override;
   void SetRequiresClear(bool requires_clear) override;
   bool IsSuitableForGpuRasterization() const override;
@@ -98,6 +99,7 @@
   float min_contents_scale_;
   gfx::Size tile_grid_size_;
   int slow_down_raster_scale_factor_for_debug_;
+  bool gather_pixel_refs_;
   // A hint about whether there are any recordings. This may be a false
   // positive.
   bool has_any_recordings_;
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc
index e5f217b..9c68137 100644
--- a/cc/resources/picture_pile_impl.cc
+++ b/cc/resources/picture_pile_impl.cc
@@ -80,17 +80,13 @@
 void PicturePileImpl::PlaybackToSharedCanvas(SkCanvas* canvas,
                                              const gfx::Rect& canvas_rect,
                                              float contents_scale) const {
-  RasterCommon(canvas,
-               NULL,
-               canvas_rect,
-               contents_scale,
-               false);
+  RasterCommon(canvas, NULL, canvas_rect, contents_scale);
 }
 
 void PicturePileImpl::RasterForAnalysis(skia::AnalysisCanvas* canvas,
                                         const gfx::Rect& canvas_rect,
                                         float contents_scale) const {
-  RasterCommon(canvas, canvas, canvas_rect, contents_scale, true);
+  RasterCommon(canvas, canvas, canvas_rect, contents_scale);
 }
 
 void PicturePileImpl::PlaybackToCanvas(SkCanvas* canvas,
@@ -99,11 +95,7 @@
   RasterSourceHelper::PrepareForPlaybackToCanvas(
       canvas, canvas_rect, gfx::Rect(tiling_.tiling_size()), contents_scale,
       background_color_, clear_canvas_with_debug_color_, requires_clear_);
-  RasterCommon(canvas,
-               NULL,
-               canvas_rect,
-               contents_scale,
-               false);
+  RasterCommon(canvas, NULL, canvas_rect, contents_scale);
 }
 
 void PicturePileImpl::CoalesceRasters(const gfx::Rect& canvas_rect,
@@ -201,12 +193,10 @@
   }
 }
 
-void PicturePileImpl::RasterCommon(
-    SkCanvas* canvas,
-    SkDrawPictureCallback* callback,
-    const gfx::Rect& canvas_rect,
-    float contents_scale,
-    bool is_analysis) const {
+void PicturePileImpl::RasterCommon(SkCanvas* canvas,
+                                   SkDrawPictureCallback* callback,
+                                   const gfx::Rect& canvas_rect,
+                                   float contents_scale) const {
   DCHECK(contents_scale >= min_contents_scale_);
 
   canvas->translate(-canvas_rect.x(), -canvas_rect.y());
diff --git a/cc/resources/picture_pile_impl.h b/cc/resources/picture_pile_impl.h
index b144a79..72f9a5c 100644
--- a/cc/resources/picture_pile_impl.h
+++ b/cc/resources/picture_pile_impl.h
@@ -140,12 +140,10 @@
                        float contents_scale,
                        PictureRegionMap* result) const;
 
-  void RasterCommon(
-      SkCanvas* canvas,
-      SkDrawPictureCallback* callback,
-      const gfx::Rect& canvas_rect,
-      float contents_scale,
-      bool is_analysis) const;
+  void RasterCommon(SkCanvas* canvas,
+                    SkDrawPictureCallback* callback,
+                    const gfx::Rect& canvas_rect,
+                    float contents_scale) const;
 
   // An internal CanRaster check that goes to the picture_map rather than
   // using the recorded_viewport hint.
diff --git a/cc/resources/recording_source.h b/cc/resources/recording_source.h
index a202e63..98e4e87 100644
--- a/cc/resources/recording_source.h
+++ b/cc/resources/recording_source.h
@@ -47,6 +47,7 @@
   virtual gfx::Size GetSize() const = 0;
   virtual void SetEmptyBounds() = 0;
   virtual void SetSlowdownRasterScaleFactor(int factor) = 0;
+  virtual void SetGatherPixelRefs(bool gather_pixel_refs) = 0;
   virtual void SetBackgroundColor(SkColor background_color) = 0;
   virtual void SetRequiresClear(bool requires_clear) = 0;
   virtual bool IsSuitableForGpuRasterization() const = 0;
diff --git a/cc/resources/texture_compressor.cc b/cc/resources/texture_compressor.cc
new file mode 100644
index 0000000..186a47d
--- /dev/null
+++ b/cc/resources/texture_compressor.cc
@@ -0,0 +1,22 @@
+// 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/texture_compressor.h"
+
+#include "base/logging.h"
+#include "cc/resources/texture_compressor_etc1.h"
+
+namespace cc {
+
+scoped_ptr<TextureCompressor> TextureCompressor::Create(Format format) {
+  switch (format) {
+    case kFormatETC1:
+      return make_scoped_ptr(new TextureCompressorETC1());
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace cc
diff --git a/cc/resources/texture_compressor.h b/cc/resources/texture_compressor.h
new file mode 100644
index 0000000..18faa40
--- /dev/null
+++ b/cc/resources/texture_compressor.h
@@ -0,0 +1,46 @@
+// 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 CC_RESOURCES_TEXTURE_COMPRESSOR_H_
+#define CC_RESOURCES_TEXTURE_COMPRESSOR_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class CC_EXPORT TextureCompressor {
+ public:
+  enum Format {
+    kFormatETC1,
+  };
+
+  enum Quality {
+    kQualityLow,
+    kQualityMedium,
+    kQualityHigh,
+  };
+
+  static scoped_ptr<TextureCompressor> Create(Format format);
+  virtual ~TextureCompressor() {}
+
+  virtual void Compress(const uint8_t* src,
+                        uint8_t* dst,
+                        int width,
+                        int height,
+                        Quality quality) = 0;
+
+ protected:
+  TextureCompressor() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TextureCompressor);
+};
+
+}  // namespace cc
+
+#endif  // CC_RESOURCES_TEXTURE_COMPRESSOR_H_
diff --git a/cc/resources/texture_compressor_etc1.cc b/cc/resources/texture_compressor_etc1.cc
new file mode 100644
index 0000000..61c4438
--- /dev/null
+++ b/cc/resources/texture_compressor_etc1.cc
@@ -0,0 +1,503 @@
+// 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.
+
+// See the following specification for details on the ETC1 format:
+// https://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
+
+#include "cc/resources/texture_compressor_etc1.h"
+
+#include <string.h>
+#include <limits>
+
+#include "base/logging.h"
+
+// Defining the following macro will cause the error metric function to weigh
+// each color channel differently depending on how the human eye can perceive
+// them. This can give a slight improvement in image quality at the cost of a
+// performance hit.
+// #define USE_PERCEIVED_ERROR_METRIC
+
+namespace {
+
+template <typename T>
+inline T clamp(T val, T min, T max) {
+  return val < min ? min : (val > max ? max : val);
+}
+
+inline uint8_t round_to_5_bits(float val) {
+  return clamp<uint8_t>(val * 31.0f / 255.0f + 0.5f, 0, 31);
+}
+
+inline uint8_t round_to_4_bits(float val) {
+  return clamp<uint8_t>(val * 15.0f / 255.0f + 0.5f, 0, 15);
+}
+
+union Color {
+  struct BgraColorType {
+    uint8_t b;
+    uint8_t g;
+    uint8_t r;
+    uint8_t a;
+  } channels;
+  uint8_t components[4];
+  uint32_t bits;
+};
+
+/*
+ * Codeword tables.
+ * See: Table 3.17.2
+ */
+static const int16_t g_codeword_tables[8][4] = {{-8, -2, 2, 8},
+                                                {-17, -5, 5, 17},
+                                                {-29, -9, 9, 29},
+                                                {-42, -13, 13, 42},
+                                                {-60, -18, 18, 60},
+                                                {-80, -24, 24, 80},
+                                                {-106, -33, 33, 106},
+                                                {-183, -47, 47, 183}};
+
+/*
+ * Maps modifier indices to pixel index values.
+ * See: Table 3.17.3
+ */
+static const uint8_t g_mod_to_pix[4] = {3, 2, 0, 1};
+
+/*
+ * The ETC1 specification index texels as follows:
+ *
+ * [a][e][i][m]     [ 0][ 4][ 8][12]
+ * [b][f][j][n] <-> [ 1][ 5][ 9][13]
+ * [c][g][k][o]     [ 2][ 6][10][14]
+ * [d][h][l][p]     [ 3][ 7][11][15]
+ *
+ * However, when extracting sub blocks from BGRA data the natural array
+ * indexing order ends up different:
+ *
+ * vertical0: [a][e][b][f]  horizontal0: [a][e][i][m]
+ *            [c][g][d][h]               [b][f][j][n]
+ * vertical1: [i][m][j][n]  horizontal1: [c][g][k][o]
+ *            [k][o][l][p]               [d][h][l][p]
+ *
+ * In order to translate from the natural array indices in a sub block to the
+ * indices (number) used by specification and hardware we use this table.
+ */
+static const uint8_t g_idx_to_num[4][8] = {
+    {0, 4, 1, 5, 2, 6, 3, 7},        // Vertical block 0.
+    {8, 12, 9, 13, 10, 14, 11, 15},  // Vertical block 1.
+    {0, 4, 8, 12, 1, 5, 9, 13},      // Horizontal block 0.
+    {2, 6, 10, 14, 3, 7, 11, 15}     // Horizontal block 1.
+};
+
+inline void WriteColors444(uint8_t* block,
+                           const Color& color0,
+                           const Color& color1) {
+  block[0] = (color0.channels.r & 0xf0) | (color1.channels.r >> 4);
+  block[1] = (color0.channels.g & 0xf0) | (color1.channels.g >> 4);
+  block[2] = (color0.channels.b & 0xf0) | (color1.channels.b >> 4);
+}
+
+inline void WriteColors555(uint8_t* block,
+                           const Color& color0,
+                           const Color& color1) {
+  // Table for conversion to 3-bit two complement format.
+  static const uint8_t two_compl_trans_table[8] = {
+      4,  // -4 (100b)
+      5,  // -3 (101b)
+      6,  // -2 (110b)
+      7,  // -1 (111b)
+      0,  //  0 (000b)
+      1,  //  1 (001b)
+      2,  //  2 (010b)
+      3,  //  3 (011b)
+  };
+
+  int16_t delta_r =
+      static_cast<int16_t>(color1.channels.r >> 3) - (color0.channels.r >> 3);
+  int16_t delta_g =
+      static_cast<int16_t>(color1.channels.g >> 3) - (color0.channels.g >> 3);
+  int16_t delta_b =
+      static_cast<int16_t>(color1.channels.b >> 3) - (color0.channels.b >> 3);
+  DCHECK(delta_r >= -4 && delta_r <= 3);
+  DCHECK(delta_g >= -4 && delta_g <= 3);
+  DCHECK(delta_b >= -4 && delta_b <= 3);
+
+  block[0] = (color0.channels.r & 0xf8) | two_compl_trans_table[delta_r + 4];
+  block[1] = (color0.channels.g & 0xf8) | two_compl_trans_table[delta_g + 4];
+  block[2] = (color0.channels.b & 0xf8) | two_compl_trans_table[delta_b + 4];
+}
+
+inline void WriteCodewordTable(uint8_t* block,
+                               uint8_t sub_block_id,
+                               uint8_t table) {
+  DCHECK_LT(sub_block_id, 2);
+  DCHECK_LT(table, 8);
+
+  uint8_t shift = (2 + (3 - sub_block_id * 3));
+  block[3] &= ~(0x07 << shift);
+  block[3] |= table << shift;
+}
+
+inline void WritePixelData(uint8_t* block, uint32_t pixel_data) {
+  block[4] |= pixel_data >> 24;
+  block[5] |= (pixel_data >> 16) & 0xff;
+  block[6] |= (pixel_data >> 8) & 0xff;
+  block[7] |= pixel_data & 0xff;
+}
+
+inline void WriteFlip(uint8_t* block, bool flip) {
+  block[3] &= ~0x01;
+  block[3] |= static_cast<uint8_t>(flip);
+}
+
+inline void WriteDiff(uint8_t* block, bool diff) {
+  block[3] &= ~0x02;
+  block[3] |= static_cast<uint8_t>(diff) << 1;
+}
+
+/**
+ * Compress and rounds BGR888 into BGR444. The resulting BGR444 color is
+ * expanded to BGR888 as it would be in hardware after decompression. The
+ * actual 444-bit data is available in the four most significant bits of each
+ * channel.
+ */
+inline Color MakeColor444(const float* bgr) {
+  uint8_t b4 = round_to_4_bits(bgr[0]);
+  uint8_t g4 = round_to_4_bits(bgr[1]);
+  uint8_t r4 = round_to_4_bits(bgr[2]);
+  Color bgr444;
+  bgr444.channels.b = (b4 << 4) | b4;
+  bgr444.channels.g = (g4 << 4) | g4;
+  bgr444.channels.r = (r4 << 4) | r4;
+  return bgr444;
+}
+
+/**
+ * Compress and rounds BGR888 into BGR555. The resulting BGR555 color is
+ * expanded to BGR888 as it would be in hardware after decompression. The
+ * actual 555-bit data is available in the five most significant bits of each
+ * channel.
+ */
+inline Color MakeColor555(const float* bgr) {
+  uint8_t b5 = round_to_5_bits(bgr[0]);
+  uint8_t g5 = round_to_5_bits(bgr[1]);
+  uint8_t r5 = round_to_5_bits(bgr[2]);
+  Color bgr555;
+  bgr555.channels.b = (b5 << 3) | (b5 >> 2);
+  bgr555.channels.g = (g5 << 3) | (g5 >> 2);
+  bgr555.channels.r = (r5 << 3) | (r5 >> 2);
+  return bgr555;
+}
+
+/**
+ * Constructs a color from a given base color and luminance value.
+ */
+inline Color MakeColor(const Color& base, int16_t lum) {
+  int b = static_cast<int>(base.channels.b) + lum;
+  int g = static_cast<int>(base.channels.g) + lum;
+  int r = static_cast<int>(base.channels.r) + lum;
+  Color color;
+  color.channels.b = static_cast<uint8_t>(clamp(b, 0, 255));
+  color.channels.g = static_cast<uint8_t>(clamp(g, 0, 255));
+  color.channels.r = static_cast<uint8_t>(clamp(r, 0, 255));
+  return color;
+}
+
+/**
+ * Calculates the error metric for two colors. A small error signals that the
+ * colors are similar to each other, a large error the signals the opposite.
+ */
+inline uint32_t GetColorError(const Color& u, const Color& v) {
+#ifdef USE_PERCEIVED_ERROR_METRIC
+  float delta_b = static_cast<float>(u.channels.b) - v.channels.b;
+  float delta_g = static_cast<float>(u.channels.g) - v.channels.g;
+  float delta_r = static_cast<float>(u.channels.r) - v.channels.r;
+  return static_cast<uint32_t>(0.299f * delta_b * delta_b +
+                               0.587f * delta_g * delta_g +
+                               0.114f * delta_r * delta_r);
+#else
+  int delta_b = static_cast<int>(u.channels.b) - v.channels.b;
+  int delta_g = static_cast<int>(u.channels.g) - v.channels.g;
+  int delta_r = static_cast<int>(u.channels.r) - v.channels.r;
+  return delta_b * delta_b + delta_g * delta_g + delta_r * delta_r;
+#endif
+}
+
+void GetAverageColor(const Color* src, float* avg_color) {
+  uint32_t sum_b = 0, sum_g = 0, sum_r = 0;
+
+  for (unsigned int i = 0; i < 8; ++i) {
+    sum_b += src[i].channels.b;
+    sum_g += src[i].channels.g;
+    sum_r += src[i].channels.r;
+  }
+
+  const float kInv8 = 1.0f / 8.0f;
+  avg_color[0] = static_cast<float>(sum_b) * kInv8;
+  avg_color[1] = static_cast<float>(sum_g) * kInv8;
+  avg_color[2] = static_cast<float>(sum_r) * kInv8;
+}
+
+void ComputeLuminance(uint8_t* block,
+                      const Color* src,
+                      const Color& base,
+                      int sub_block_id,
+                      const uint8_t* idx_to_num_tab) {
+  uint32_t best_tbl_err = std::numeric_limits<uint32_t>::max();
+  uint8_t best_tbl_idx = 0;
+  uint8_t best_mod_idx[8][8];  // [table][texel]
+
+  // Try all codeword tables to find the one giving the best results for this
+  // block.
+  for (unsigned int tbl_idx = 0; tbl_idx < 8; ++tbl_idx) {
+    // Pre-compute all the candidate colors; combinations of the base color and
+    // all available luminance values.
+    Color candidate_color[4];  // [modifier]
+    for (unsigned int mod_idx = 0; mod_idx < 4; ++mod_idx) {
+      int16_t lum = g_codeword_tables[tbl_idx][mod_idx];
+      candidate_color[mod_idx] = MakeColor(base, lum);
+    }
+
+    uint32_t tbl_err = 0;
+
+    for (unsigned int i = 0; i < 8; ++i) {
+      // Try all modifiers in the current table to find which one gives the
+      // smallest error.
+      uint32_t best_mod_err = std::numeric_limits<uint32_t>::max();
+      for (unsigned int mod_idx = 0; mod_idx < 4; ++mod_idx) {
+        const Color& color = candidate_color[mod_idx];
+
+        uint32_t mod_err = GetColorError(src[i], color);
+        if (mod_err < best_mod_err) {
+          best_mod_idx[tbl_idx][i] = mod_idx;
+          best_mod_err = mod_err;
+
+          if (mod_err == 0)
+            break;  // We cannot do any better than this.
+        }
+      }
+
+      tbl_err += best_mod_err;
+      if (tbl_err > best_tbl_err)
+        break;  // We're already doing worse than the best table so skip.
+    }
+
+    if (tbl_err < best_tbl_err) {
+      best_tbl_err = tbl_err;
+      best_tbl_idx = tbl_idx;
+
+      if (tbl_err == 0)
+        break;  // We cannot do any better than this.
+    }
+  }
+
+  WriteCodewordTable(block, sub_block_id, best_tbl_idx);
+
+  uint32_t pix_data = 0;
+
+  for (unsigned int i = 0; i < 8; ++i) {
+    uint8_t mod_idx = best_mod_idx[best_tbl_idx][i];
+    uint8_t pix_idx = g_mod_to_pix[mod_idx];
+
+    uint32_t lsb = pix_idx & 0x1;
+    uint32_t msb = pix_idx >> 1;
+
+    // Obtain the texel number as specified in the standard.
+    int texel_num = idx_to_num_tab[i];
+    pix_data |= msb << (texel_num + 16);
+    pix_data |= lsb << (texel_num);
+  }
+
+  WritePixelData(block, pix_data);
+}
+
+/**
+ * Tries to compress the block under the assumption that it's a single color
+ * block. If it's not the function will bail out without writing anything to
+ * the destination buffer.
+ */
+bool TryCompressSolidBlock(uint8_t* dst, const Color* src) {
+  for (unsigned int i = 1; i < 16; ++i) {
+    if (src[i].bits != src[0].bits)
+      return false;
+  }
+
+  // Clear destination buffer so that we can "or" in the results.
+  memset(dst, 0, 8);
+
+  float src_color_float[3] = {static_cast<float>(src->channels.b),
+                              static_cast<float>(src->channels.g),
+                              static_cast<float>(src->channels.r)};
+  Color base = MakeColor555(src_color_float);
+
+  WriteDiff(dst, true);
+  WriteFlip(dst, false);
+  WriteColors555(dst, base, base);
+
+  uint8_t best_tbl_idx = 0;
+  uint8_t best_mod_idx = 0;
+  uint32_t best_mod_err = std::numeric_limits<uint32_t>::max();
+
+  // Try all codeword tables to find the one giving the best results for this
+  // block.
+  for (unsigned int tbl_idx = 0; tbl_idx < 8; ++tbl_idx) {
+    // Try all modifiers in the current table to find which one gives the
+    // smallest error.
+    for (unsigned int mod_idx = 0; mod_idx < 4; ++mod_idx) {
+      int16_t lum = g_codeword_tables[tbl_idx][mod_idx];
+      const Color& color = MakeColor(base, lum);
+
+      uint32_t mod_err = GetColorError(*src, color);
+      if (mod_err < best_mod_err) {
+        best_tbl_idx = tbl_idx;
+        best_mod_idx = mod_idx;
+        best_mod_err = mod_err;
+
+        if (mod_err == 0)
+          break;  // We cannot do any better than this.
+      }
+    }
+
+    if (best_mod_err == 0)
+      break;
+  }
+
+  WriteCodewordTable(dst, 0, best_tbl_idx);
+  WriteCodewordTable(dst, 1, best_tbl_idx);
+
+  uint8_t pix_idx = g_mod_to_pix[best_mod_idx];
+  uint32_t lsb = pix_idx & 0x1;
+  uint32_t msb = pix_idx >> 1;
+
+  uint32_t pix_data = 0;
+  for (unsigned int i = 0; i < 2; ++i) {
+    for (unsigned int j = 0; j < 8; ++j) {
+      // Obtain the texel number as specified in the standard.
+      int texel_num = g_idx_to_num[i][j];
+      pix_data |= msb << (texel_num + 16);
+      pix_data |= lsb << (texel_num);
+    }
+  }
+
+  WritePixelData(dst, pix_data);
+  return true;
+}
+
+void CompressBlock(uint8_t* dst, const Color* ver_src, const Color* hor_src) {
+  if (TryCompressSolidBlock(dst, ver_src))
+    return;
+
+  const Color* sub_block_src[4] = {ver_src, ver_src + 8, hor_src, hor_src + 8};
+
+  Color sub_block_avg[4];
+  bool use_differential[2] = {true, true};
+
+  // Compute the average color for each sub block and determine if differential
+  // coding can be used.
+  for (unsigned int i = 0, j = 1; i < 4; i += 2, j += 2) {
+    float avg_color_0[3];
+    GetAverageColor(sub_block_src[i], avg_color_0);
+    Color avg_color_555_0 = MakeColor555(avg_color_0);
+
+    float avg_color_1[3];
+    GetAverageColor(sub_block_src[j], avg_color_1);
+    Color avg_color_555_1 = MakeColor555(avg_color_1);
+
+    for (unsigned int light_idx = 0; light_idx < 3; ++light_idx) {
+      int u = avg_color_555_0.components[light_idx] >> 3;
+      int v = avg_color_555_1.components[light_idx] >> 3;
+
+      int component_diff = v - u;
+      if (component_diff < -4 || component_diff > 3) {
+        use_differential[i / 2] = false;
+        sub_block_avg[i] = MakeColor444(avg_color_0);
+        sub_block_avg[j] = MakeColor444(avg_color_1);
+      } else {
+        sub_block_avg[i] = avg_color_555_0;
+        sub_block_avg[j] = avg_color_555_1;
+      }
+    }
+  }
+
+  // Compute the error of each sub block before adjusting for luminance. These
+  // error values are later used for determining if we should flip the sub
+  // block or not.
+  uint32_t sub_block_err[4] = {0};
+  for (unsigned int i = 0; i < 4; ++i) {
+    for (unsigned int j = 0; j < 8; ++j) {
+      sub_block_err[i] += GetColorError(sub_block_avg[i], sub_block_src[i][j]);
+    }
+  }
+
+  bool flip =
+      sub_block_err[2] + sub_block_err[3] < sub_block_err[0] + sub_block_err[1];
+
+  // Clear destination buffer so that we can "or" in the results.
+  memset(dst, 0, 8);
+
+  WriteDiff(dst, use_differential[!!flip]);
+  WriteFlip(dst, flip);
+
+  uint8_t sub_block_off_0 = flip ? 2 : 0;
+  uint8_t sub_block_off_1 = sub_block_off_0 + 1;
+
+  if (use_differential[!!flip]) {
+    WriteColors555(dst, sub_block_avg[sub_block_off_0],
+                   sub_block_avg[sub_block_off_1]);
+  } else {
+    WriteColors444(dst, sub_block_avg[sub_block_off_0],
+                   sub_block_avg[sub_block_off_1]);
+  }
+
+  // Compute luminance for the first sub block.
+  ComputeLuminance(dst, sub_block_src[sub_block_off_0],
+                   sub_block_avg[sub_block_off_0], 0,
+                   g_idx_to_num[sub_block_off_0]);
+  // Compute luminance for the second sub block.
+  ComputeLuminance(dst, sub_block_src[sub_block_off_1],
+                   sub_block_avg[sub_block_off_1], 1,
+                   g_idx_to_num[sub_block_off_1]);
+}
+
+}  // namespace
+
+namespace cc {
+
+void TextureCompressorETC1::Compress(const uint8_t* src,
+                                     uint8_t* dst,
+                                     int width,
+                                     int height,
+                                     Quality quality) {
+  DCHECK(width >= 4 && (width & 3) == 0);
+  DCHECK(height >= 4 && (height & 3) == 0);
+
+  Color ver_blocks[16];
+  Color hor_blocks[16];
+
+  for (int y = 0; y < height; y += 4, src += width * 4 * 4) {
+    for (int x = 0; x < width; x += 4, dst += 8) {
+      const Color* row0 = reinterpret_cast<const Color*>(src + x * 4);
+      const Color* row1 = row0 + width;
+      const Color* row2 = row1 + width;
+      const Color* row3 = row2 + width;
+
+      memcpy(ver_blocks, row0, 8);
+      memcpy(ver_blocks + 2, row1, 8);
+      memcpy(ver_blocks + 4, row2, 8);
+      memcpy(ver_blocks + 6, row3, 8);
+      memcpy(ver_blocks + 8, row0 + 2, 8);
+      memcpy(ver_blocks + 10, row1 + 2, 8);
+      memcpy(ver_blocks + 12, row2 + 2, 8);
+      memcpy(ver_blocks + 14, row3 + 2, 8);
+
+      memcpy(hor_blocks, row0, 16);
+      memcpy(hor_blocks + 4, row1, 16);
+      memcpy(hor_blocks + 8, row2, 16);
+      memcpy(hor_blocks + 12, row3, 16);
+
+      CompressBlock(dst, ver_blocks, hor_blocks);
+    }
+  }
+}
+
+}  // namespace cc
diff --git a/cc/resources/texture_compressor_etc1.h b/cc/resources/texture_compressor_etc1.h
new file mode 100644
index 0000000..c457562
--- /dev/null
+++ b/cc/resources/texture_compressor_etc1.h
@@ -0,0 +1,31 @@
+// 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 CC_RESOURCES_TEXTURE_COMPRESSOR_ETC1_H_
+#define CC_RESOURCES_TEXTURE_COMPRESSOR_ETC1_H_
+
+#include "cc/resources/texture_compressor.h"
+
+namespace cc {
+
+class CC_EXPORT TextureCompressorETC1 : public TextureCompressor {
+ public:
+  TextureCompressorETC1() {}
+
+  // Compress a texture using ETC1. Note that the |quality| parameter is
+  // ignored. The current implementation does not support different quality
+  // settings.
+  void Compress(const uint8_t* src,
+                uint8_t* dst,
+                int width,
+                int height,
+                Quality quality) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TextureCompressorETC1);
+};
+
+}  // namespace cc
+
+#endif  // CC_RESOURCES_TEXTURE_COMPRESSOR_ETC1_H_
diff --git a/cc/resources/texture_compressor_perftest.cc b/cc/resources/texture_compressor_perftest.cc
new file mode 100644
index 0000000..7d68bd6
--- /dev/null
+++ b/cc/resources/texture_compressor_perftest.cc
@@ -0,0 +1,103 @@
+// 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/logging.h"
+#include "cc/debug/lap_timer.h"
+#include "cc/resources/texture_compressor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace cc {
+namespace {
+
+const int kTimeLimitMillis = 2000;
+const int kWarmupRuns = 5;
+const int kTimeCheckInterval = 10;
+
+const int kImageWidth = 256;
+const int kImageHeight = 256;
+const int kImageSizeInBytes = kImageWidth * kImageHeight * 4;
+
+const TextureCompressor::Quality kQualities[] = {
+    TextureCompressor::kQualityLow,
+    TextureCompressor::kQualityMedium,
+    TextureCompressor::kQualityHigh};
+
+std::string FormatName(TextureCompressor::Format format) {
+  switch (format) {
+    case TextureCompressor::kFormatETC1:
+      return "ETC1";
+  }
+
+  NOTREACHED();
+  return "";
+}
+
+std::string QualityName(TextureCompressor::Quality quality) {
+  switch (quality) {
+    case TextureCompressor::kQualityLow:
+      return "Low";
+    case TextureCompressor::kQualityMedium:
+      return "Medium";
+    case TextureCompressor::kQualityHigh:
+      return "High";
+  }
+
+  NOTREACHED();
+  return "";
+}
+
+class TextureCompressorPerfTest
+    : public testing::TestWithParam<TextureCompressor::Format> {
+ public:
+  TextureCompressorPerfTest()
+      : timer_(kWarmupRuns,
+               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+               kTimeCheckInterval) {}
+
+  void SetUp() override {
+    TextureCompressor::Format format = GetParam();
+    compressor_ = TextureCompressor::Create(format);
+  }
+
+  void RunTest(const std::string& name, TextureCompressor::Quality quality) {
+    timer_.Reset();
+    do {
+      compressor_->Compress(src_, dst_, kImageWidth, kImageHeight, quality);
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
+
+    std::string str = FormatName(GetParam()) + " " + QualityName(quality);
+    perf_test::PrintResult("Compress256x256", name, str, timer_.MsPerLap(),
+                           "us", true);
+  }
+
+ protected:
+  LapTimer timer_;
+  scoped_ptr<TextureCompressor> compressor_;
+  uint8_t src_[kImageSizeInBytes];
+  uint8_t dst_[kImageSizeInBytes];
+};
+
+TEST_P(TextureCompressorPerfTest, Compress256x256Image) {
+  for (int i = 0; i < kImageSizeInBytes; ++i)
+    src_[i] = i % 256;
+
+  for (auto& quality : kQualities)
+    RunTest("Image", quality);
+}
+
+TEST_P(TextureCompressorPerfTest, Compress256x256SolidImage) {
+  memset(src_, 0, kImageSizeInBytes);
+
+  for (auto& quality : kQualities)
+    RunTest("SolidImage", quality);
+}
+
+INSTANTIATE_TEST_CASE_P(TextureCompressorPerfTests,
+                        TextureCompressorPerfTest,
+                        ::testing::Values(TextureCompressor::kFormatETC1));
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/resources/tile.cc b/cc/resources/tile.cc
index f86330e..45b4a30 100644
--- a/cc/resources/tile.cc
+++ b/cc/resources/tile.cc
@@ -48,7 +48,8 @@
       "cc::Tile", this);
 }
 
-void Tile::AsValueInto(base::trace_event::TracedValue* res) const {
+void Tile::AsValueWithPriorityInto(const TilePriority& priority,
+                                   base::trace_event::TracedValue* res) const {
   TracedValue::MakeDictIntoImplicitSnapshotWithCategory(
       TRACE_DISABLED_BY_DEFAULT("cc.debug"), res, "cc::Tile", this);
   TracedValue::SetIDRef(raster_source_.get(), res, "picture_pile");
@@ -58,6 +59,8 @@
 
   res->SetInteger("layer_id", layer_id_);
 
+  // TODO(vmpstr): Remove active and pending priority once tracing is using
+  // combined priority or at least can support both.
   res->BeginDictionary("active_priority");
   priority_[ACTIVE_TREE].AsValueInto(res);
   res->EndDictionary();
@@ -66,6 +69,10 @@
   priority_[PENDING_TREE].AsValueInto(res);
   res->EndDictionary();
 
+  res->BeginDictionary("combined_priority");
+  priority.AsValueInto(res);
+  res->EndDictionary();
+
   res->BeginDictionary("draw_info");
   draw_info_.AsValueInto(res);
   res->EndDictionary();
diff --git a/cc/resources/tile.h b/cc/resources/tile.h
index c3ca623..dcc8566 100644
--- a/cc/resources/tile.h
+++ b/cc/resources/tile.h
@@ -92,7 +92,8 @@
            !draw_info_.IsReadyToDraw();
   }
 
-  void AsValueInto(base::trace_event::TracedValue* dict) const;
+  void AsValueWithPriorityInto(const TilePriority& priority,
+                               base::trace_event::TracedValue* dict) const;
 
   inline bool IsReadyToDraw() const { return draw_info_.IsReadyToDraw(); }
 
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc
index 7ea86f0..71f2e83 100644
--- a/cc/resources/tile_manager.cc
+++ b/cc/resources/tile_manager.cc
@@ -366,15 +366,16 @@
     scoped_ptr<RasterTilePriorityQueue> raster_priority_queue(
         client_->BuildRasterQueue(global_state_.tree_priority,
                                   RasterTilePriorityQueue::Type::ALL));
-    // Inform the client that will likely require a draw if the top tile is
-    // required for draw.
-    client_->SetIsLikelyToRequireADraw(
-        !raster_priority_queue->IsEmpty() &&
-        raster_priority_queue->Top()->required_for_draw());
     AssignGpuMemoryToTiles(raster_priority_queue.get(),
                            scheduled_raster_task_limit_,
                            &tiles_that_need_to_be_rasterized);
 
+    // Inform the client that will likely require a draw if the highest priority
+    // tile that will be rasterized is required for draw.
+    client_->SetIsLikelyToRequireADraw(
+        !tiles_that_need_to_be_rasterized.empty() &&
+        (*tiles_that_need_to_be_rasterized.begin())->required_for_draw());
+
     // Schedule tile tasks.
     ScheduleTasks(tiles_that_need_to_be_rasterized);
 
diff --git a/cc/resources/tile_manager_perftest.cc b/cc/resources/tile_manager_perftest.cc
index 87d1837..236f918 100644
--- a/cc/resources/tile_manager_perftest.cc
+++ b/cc/resources/tile_manager_perftest.cc
@@ -18,6 +18,7 @@
 #include "cc/test/fake_tile_manager_client.h"
 #include "cc/test/impl_side_painting_settings.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_tile_priorities.h"
 #include "cc/trees/layer_tree_impl.h"
 
@@ -90,7 +91,8 @@
         proxy_(base::MessageLoopProxy::current()),
         host_impl_(ImplSidePaintingSettings(10000),
                    &proxy_,
-                   &shared_bitmap_manager_),
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_),
         timer_(kWarmupRuns,
                base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
                kTimeCheckInterval) {}
@@ -407,6 +409,7 @@
   GlobalStateThatImpactsTilePriority global_state_;
 
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   TileMemoryLimitPolicy memory_limit_policy_;
   int max_tiles_;
   int id_;
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc
index 19ccf24..e826f55 100644
--- a/cc/resources/tile_manager_unittest.cc
+++ b/cc/resources/tile_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "cc/test/fake_tile_manager.h"
 #include "cc/test/impl_side_painting_settings.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_tile_priorities.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -38,7 +39,10 @@
         ready_to_activate_(false),
         id_(7),
         proxy_(base::MessageLoopProxy::current()),
-        host_impl_(LowResTilingsSettings(), &proxy_, &shared_bitmap_manager_) {}
+        host_impl_(LowResTilingsSettings(),
+                   &proxy_,
+                   &shared_bitmap_manager_,
+                   &task_graph_runner_) {}
 
   void SetTreePriority(TreePriority tree_priority) {
     GlobalStateThatImpactsTilePriority state;
@@ -137,6 +141,7 @@
   GlobalStateThatImpactsTilePriority global_state_;
 
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   TileMemoryLimitPolicy memory_limit_policy_;
   int max_tiles_;
   bool ready_to_activate_;
@@ -1277,5 +1282,42 @@
   EXPECT_TRUE(have_tiles[TilePriority::EVENTUALLY]);
 }
 
+TEST_F(TileManagerTilePriorityQueueTest, SetIsLikelyToRequireADraw) {
+  const gfx::Size layer_bounds(1000, 1000);
+  host_impl_.SetViewportSize(layer_bounds);
+  SetupDefaultTrees(layer_bounds);
+
+  // Verify that the queue has a required for draw tile at Top.
+  scoped_ptr<RasterTilePriorityQueue> queue(host_impl_.BuildRasterQueue(
+      SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
+  EXPECT_FALSE(queue->IsEmpty());
+  EXPECT_TRUE(queue->Top()->required_for_draw());
+
+  EXPECT_FALSE(host_impl_.is_likely_to_require_a_draw());
+  host_impl_.tile_manager()->PrepareTiles(host_impl_.global_tile_state());
+  EXPECT_TRUE(host_impl_.is_likely_to_require_a_draw());
+}
+
+TEST_F(TileManagerTilePriorityQueueTest,
+       NoSetIsLikelyToRequireADrawOnZeroMemoryBudget) {
+  const gfx::Size layer_bounds(1000, 1000);
+  host_impl_.SetViewportSize(layer_bounds);
+  SetupDefaultTrees(layer_bounds);
+
+  // Verify that the queue has a required for draw tile at Top.
+  scoped_ptr<RasterTilePriorityQueue> queue(host_impl_.BuildRasterQueue(
+      SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
+  EXPECT_FALSE(queue->IsEmpty());
+  EXPECT_TRUE(queue->Top()->required_for_draw());
+
+  ManagedMemoryPolicy policy = host_impl_.ActualManagedMemoryPolicy();
+  policy.bytes_limit_when_visible = 0;
+  host_impl_.SetMemoryPolicy(policy);
+
+  EXPECT_FALSE(host_impl_.is_likely_to_require_a_draw());
+  host_impl_.tile_manager()->PrepareTiles(host_impl_.global_tile_state());
+  EXPECT_FALSE(host_impl_.is_likely_to_require_a_draw());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/resources/tile_task_worker_pool.cc b/cc/resources/tile_task_worker_pool.cc
index 2775386..0fbc7be 100644
--- a/cc/resources/tile_task_worker_pool.cc
+++ b/cc/resources/tile_task_worker_pool.cc
@@ -6,11 +6,7 @@
 
 #include <algorithm>
 
-#include "base/lazy_instance.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/simple_thread.h"
 #include "base/trace_event/trace_event.h"
-#include "cc/base/scoped_ptr_deque.h"
 #include "cc/resources/raster_source.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -19,41 +15,6 @@
 namespace cc {
 namespace {
 
-base::ThreadPriority g_worker_thread_priority = base::kThreadPriority_Normal;
-
-class TileTaskGraphRunner : public TaskGraphRunner,
-                            public base::DelegateSimpleThread::Delegate {
- public:
-  TileTaskGraphRunner() {
-    size_t num_threads = TileTaskWorkerPool::GetNumWorkerThreads();
-    while (workers_.size() < num_threads) {
-      scoped_ptr<base::DelegateSimpleThread> worker =
-          make_scoped_ptr(new base::DelegateSimpleThread(
-              this, base::StringPrintf(
-                        "CompositorTileWorker%u",
-                        static_cast<unsigned>(workers_.size() + 1)).c_str()));
-      worker->Start();
-      worker->SetThreadPriority(g_worker_thread_priority);
-      workers_.push_back(worker.Pass());
-    }
-  }
-
-  ~TileTaskGraphRunner() override { NOTREACHED(); }
-
- private:
-  // Overridden from base::DelegateSimpleThread::Delegate:
-  void Run() override { TaskGraphRunner::Run(); }
-
-  ScopedPtrDeque<base::DelegateSimpleThread> workers_;
-};
-
-base::LazyInstance<TileTaskGraphRunner>::Leaky g_task_graph_runner =
-    LAZY_INSTANCE_INITIALIZER;
-
-const int kDefaultNumWorkerThreads = 1;
-
-int g_num_worker_threads = 0;
-
 class TaskSetFinishedTaskImpl : public TileTask {
  public:
   explicit TaskSetFinishedTaskImpl(
@@ -107,33 +68,6 @@
 }
 
 // static
-void TileTaskWorkerPool::SetNumWorkerThreads(int num_threads) {
-  DCHECK_LT(0, num_threads);
-  DCHECK_EQ(0, g_num_worker_threads);
-
-  g_num_worker_threads = num_threads;
-}
-
-// static
-int TileTaskWorkerPool::GetNumWorkerThreads() {
-  if (!g_num_worker_threads)
-    g_num_worker_threads = kDefaultNumWorkerThreads;
-
-  return g_num_worker_threads;
-}
-
-// static
-void TileTaskWorkerPool::SetWorkerThreadPriority(
-    base::ThreadPriority priority) {
-  g_worker_thread_priority = priority;
-}
-
-// static
-TaskGraphRunner* TileTaskWorkerPool::GetTaskGraphRunner() {
-  return g_task_graph_runner.Pointer();
-}
-
-// static
 scoped_refptr<TileTask> TileTaskWorkerPool::CreateTaskSetFinishedTask(
     base::SequencedTaskRunner* task_runner,
     const base::Closure& on_task_set_finished_callback) {
diff --git a/cc/resources/tile_task_worker_pool.h b/cc/resources/tile_task_worker_pool.h
index 915a3fa..90adfd1 100644
--- a/cc/resources/tile_task_worker_pool.h
+++ b/cc/resources/tile_task_worker_pool.h
@@ -5,7 +5,6 @@
 #ifndef CC_RESOURCES_TILE_TASK_WORKER_POOL_H_
 #define CC_RESOURCES_TILE_TASK_WORKER_POOL_H_
 
-#include "base/threading/platform_thread.h"
 #include "cc/resources/tile_task_runner.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -27,20 +26,6 @@
   TileTaskWorkerPool();
   virtual ~TileTaskWorkerPool();
 
-  // Set the number of threads to use for the global TaskGraphRunner instance.
-  // This can only be called once and must be called prior to
-  // GetNumWorkerThreads().
-  static void SetNumWorkerThreads(int num_threads);
-
-  // Returns the number of threads used for the global TaskGraphRunner instance.
-  static int GetNumWorkerThreads();
-
-  // Set the priority of worker threads.
-  static void SetWorkerThreadPriority(base::ThreadPriority priority);
-
-  // Returns a pointer to the global TaskGraphRunner instance.
-  static TaskGraphRunner* GetTaskGraphRunner();
-
   // Utility function that can be used to create a "Task set finished" task that
   // posts |callback| to |task_runner| when run.
   static scoped_refptr<TileTask> CreateTaskSetFinishedTask(
diff --git a/cc/resources/tile_task_worker_pool_unittest.cc b/cc/resources/tile_task_worker_pool_unittest.cc
index 72f9d20..3d74daf 100644
--- a/cc/resources/tile_task_worker_pool_unittest.cc
+++ b/cc/resources/tile_task_worker_pool_unittest.cc
@@ -27,6 +27,7 @@
 #include "cc/test/fake_picture_pile_impl.h"
 #include "cc/test/test_gpu_memory_buffer_manager.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -142,39 +143,38 @@
       case TILE_TASK_WORKER_POOL_TYPE_PIXEL_BUFFER:
         Create3dOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = PixelBufferTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(),
-            TileTaskWorkerPool::GetTaskGraphRunner(), context_provider_.get(),
-            resource_provider_.get(), kMaxTransferBufferUsageBytes);
+            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            context_provider_.get(), resource_provider_.get(),
+            kMaxTransferBufferUsageBytes);
         break;
       case TILE_TASK_WORKER_POOL_TYPE_ZERO_COPY:
         Create3dOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = ZeroCopyTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(),
-            TileTaskWorkerPool::GetTaskGraphRunner(), resource_provider_.get());
+            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            resource_provider_.get());
         break;
       case TILE_TASK_WORKER_POOL_TYPE_ONE_COPY:
         Create3dOutputSurfaceAndResourceProvider();
         staging_resource_pool_ = ResourcePool::Create(resource_provider_.get(),
                                                       GL_TEXTURE_2D);
         tile_task_worker_pool_ = OneCopyTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(),
-            TileTaskWorkerPool::GetTaskGraphRunner(), context_provider_.get(),
-            resource_provider_.get(), staging_resource_pool_.get());
+            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            context_provider_.get(), resource_provider_.get(),
+            staging_resource_pool_.get());
         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(
-            base::MessageLoopProxy::current().get(),
-            TileTaskWorkerPool::GetTaskGraphRunner(),
+            base::MessageLoopProxy::current().get(), &task_graph_runner_,
             static_cast<GpuRasterizer*>(rasterizer_.get()));
         break;
       case TILE_TASK_WORKER_POOL_TYPE_BITMAP:
         CreateSoftwareOutputSurfaceAndResourceProvider();
         tile_task_worker_pool_ = BitmapTileTaskWorkerPool::Create(
-            base::MessageLoopProxy::current().get(),
-            TileTaskWorkerPool::GetTaskGraphRunner(), resource_provider_.get());
+            base::MessageLoopProxy::current().get(), &task_graph_runner_,
+            resource_provider_.get());
         break;
     }
 
@@ -331,6 +331,7 @@
   scoped_ptr<TileTaskWorkerPool> tile_task_worker_pool_;
   TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   base::CancelableClosure timeout_;
   UniqueNotifier all_tile_tasks_finished_;
   int timeout_seconds_;
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 3b20b79..68214cd 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -304,24 +304,21 @@
 }
 
 void Scheduler::SetupNextBeginFrameIfNeeded() {
-  if (!task_runner_.get())
-    return;
-
-  if (state_machine_.ShouldSetNeedsBeginFrames(
-          frame_source_->NeedsBeginFrames())) {
-    frame_source_->SetNeedsBeginFrames(state_machine_.BeginFrameNeeded());
-    if (!frame_source_->NeedsBeginFrames()) {
+  // Never call SetNeedsBeginFrames if the frame source already has the right
+  // value.
+  if (frame_source_->NeedsBeginFrames() != state_machine_.BeginFrameNeeded()) {
+    if (state_machine_.BeginFrameNeeded()) {
+      // Call SetNeedsBeginFrames(true) as soon as possible.
+      frame_source_->SetNeedsBeginFrames(true);
+    } else if (state_machine_.begin_impl_frame_state() ==
+               SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE) {
+      // Call SetNeedsBeginFrames(false) in between frames only.
+      frame_source_->SetNeedsBeginFrames(false);
       client_->SendBeginMainFrameNotExpectedSoon();
     }
   }
 
-  if (state_machine_.begin_impl_frame_state() ==
-      SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) {
-    frame_source_->DidFinishFrame(begin_retro_frame_args_.size());
-  }
-
   PostBeginRetroFrameIfNeeded();
-  SetupPollingMechanisms();
 }
 
 // We may need to poll when we can't rely on BeginFrame to advance certain
@@ -537,7 +534,7 @@
     state_machine_.SetSkipNextBeginMainFrameToReduceLatency();
   }
 
-  state_machine_.OnBeginImplFrame(begin_impl_frame_args_);
+  state_machine_.OnBeginImplFrame();
   devtools_instrumentation::DidBeginFrame(layer_tree_host_id_);
   client_->WillBeginImplFrame(begin_impl_frame_args_);
 
@@ -627,19 +624,11 @@
           "461509 Scheduler::OnBeginImplFrameDeadline1"));
   state_machine_.OnBeginImplFrameDeadline();
   ProcessScheduledActions();
-
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461509 Scheduler::OnBeginImplFrameDeadline2"));
   state_machine_.OnBeginImplFrameIdle();
   ProcessScheduledActions();
 
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461509 Scheduler::OnBeginImplFrameDeadline3"));
   client_->DidBeginImplFrameDeadline();
+  frame_source_->DidFinishFrame(begin_retro_frame_args_.size());
 }
 
 void Scheduler::PollForAnticipatedDrawTriggers() {
@@ -679,10 +668,6 @@
 
   SchedulerStateMachine::Action action;
   do {
-    // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "461509 Scheduler::ProcessScheduledActions1"));
     action = state_machine_.NextAction();
     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
                  "SchedulerStateMachine",
@@ -697,24 +682,12 @@
     switch (action) {
       case SchedulerStateMachine::ACTION_NONE:
         break;
-      case SchedulerStateMachine::ACTION_ANIMATE: {
-        // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
-        // fixed.
-        tracked_objects::ScopedTracker tracking_profile2(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "461509 Scheduler::ProcessScheduledActions2"));
+      case SchedulerStateMachine::ACTION_ANIMATE:
         client_->ScheduledActionAnimate();
         break;
-      }
-      case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME: {
-        // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
-        // fixed.
-        tracked_objects::ScopedTracker tracking_profile3(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "461509 Scheduler::ProcessScheduledActions3"));
+      case SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME:
         client_->ScheduledActionSendBeginMainFrame();
         break;
-      }
       case SchedulerStateMachine::ACTION_COMMIT: {
         // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
         // fixed.
@@ -724,15 +697,9 @@
         client_->ScheduledActionCommit();
         break;
       }
-      case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE: {
-        // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
-        // fixed.
-        tracked_objects::ScopedTracker tracking_profile5(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "461509 Scheduler::ProcessScheduledActions5"));
+      case SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE:
         client_->ScheduledActionActivateSyncTree();
         break;
-      }
       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: {
         // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
         // fixed.
@@ -742,52 +709,29 @@
         DrawAndSwapIfPossible();
         break;
       }
-      case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: {
-        // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
-        // fixed.
-        tracked_objects::ScopedTracker tracking_profile7(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "461509 Scheduler::ProcessScheduledActions7"));
+      case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
         client_->ScheduledActionDrawAndSwapForced();
         break;
-      }
       case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
         // No action is actually performed, but this allows the state machine to
         // advance out of its waiting to draw state without actually drawing.
         break;
-      case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: {
-        // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
-        // fixed.
-        tracked_objects::ScopedTracker tracking_profile8(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "461509 Scheduler::ProcessScheduledActions8"));
+      case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
         client_->ScheduledActionBeginOutputSurfaceCreation();
         break;
-      }
-      case SchedulerStateMachine::ACTION_PREPARE_TILES: {
-        // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is
-        // fixed.
-        tracked_objects::ScopedTracker tracking_profile9(
-            FROM_HERE_WITH_EXPLICIT_FUNCTION(
-                "461509 Scheduler::ProcessScheduledActions9"));
+      case SchedulerStateMachine::ACTION_PREPARE_TILES:
         client_->ScheduledActionPrepareTiles();
         break;
-      }
     }
   } while (action != SchedulerStateMachine::ACTION_NONE);
 
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
-  tracked_objects::ScopedTracker tracking_profile10(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461509 Scheduler::ProcessScheduledActions10"));
-  SetupNextBeginFrameIfNeeded();
+  SetupPollingMechanisms();
+
   client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
 
-  // TODO(robliao): Remove ScopedTracker below once crbug.com/461509 is fixed.
-  tracked_objects::ScopedTracker tracking_profile11(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "461509 Scheduler::ProcessScheduledActions11"));
   RescheduleBeginImplFrameDeadlineIfNeeded();
+
+  SetupNextBeginFrameIfNeeded();
 }
 
 scoped_refptr<base::trace_event::ConvertableToTraceFormat> Scheduler::AsValue()
@@ -800,7 +744,7 @@
 
 void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const {
   state->BeginDictionary("state_machine");
-  state_machine_.AsValueInto(state, Now());
+  state_machine_.AsValueInto(state);
   state->EndDictionary();
 
   // Only trace frame sources when explicitly enabled - http://crbug.com/420607
@@ -834,6 +778,23 @@
   begin_impl_frame_args_.AsValueInto(state);
   state->EndDictionary();
 
+  base::TimeTicks now = Now();
+  base::TimeTicks frame_time = begin_impl_frame_args_.frame_time;
+  base::TimeTicks deadline = begin_impl_frame_args_.deadline;
+  base::TimeDelta interval = begin_impl_frame_args_.interval;
+  state->BeginDictionary("major_timestamps_in_ms");
+  state->SetDouble("0_interval", interval.InMillisecondsF());
+  state->SetDouble("1_now_to_deadline", (deadline - now).InMillisecondsF());
+  state->SetDouble("2_frame_time_to_now", (now - frame_time).InMillisecondsF());
+  state->SetDouble("3_frame_time_to_deadline",
+                   (deadline - frame_time).InMillisecondsF());
+  state->SetDouble("4_now", (now - base::TimeTicks()).InMillisecondsF());
+  state->SetDouble("5_frame_time",
+                   (frame_time - base::TimeTicks()).InMillisecondsF());
+  state->SetDouble("6_deadline",
+                   (deadline - base::TimeTicks()).InMillisecondsF());
+  state->EndDictionary();
+
   state->EndDictionary();
 
   state->BeginDictionary("client_state");
diff --git a/cc/scheduler/scheduler_settings.cc b/cc/scheduler/scheduler_settings.cc
index 39a98b4..c6c8e8e 100644
--- a/cc/scheduler/scheduler_settings.cc
+++ b/cc/scheduler/scheduler_settings.cc
@@ -5,7 +5,6 @@
 #include "cc/scheduler/scheduler_settings.h"
 
 #include "base/trace_event/trace_event_argument.h"
-#include "cc/trees/layer_tree_settings.h"
 
 namespace cc {
 
@@ -21,23 +20,6 @@
       background_frame_interval(base::TimeDelta::FromSeconds(1)) {
 }
 
-SchedulerSettings::SchedulerSettings(const LayerTreeSettings& settings)
-    : use_external_begin_frame_source(settings.use_external_begin_frame_source),
-      main_frame_before_activation_enabled(
-          settings.main_frame_before_activation_enabled),
-      impl_side_painting(settings.impl_side_painting),
-      timeout_and_draw_when_animation_checkerboards(
-          settings.timeout_and_draw_when_animation_checkerboards),
-      maximum_number_of_failed_draws_before_draw_is_forced_(
-          settings.maximum_number_of_failed_draws_before_draw_is_forced_),
-      using_synchronous_renderer_compositor(
-          settings.using_synchronous_renderer_compositor),
-      throttle_frame_production(settings.throttle_frame_production),
-      main_thread_should_always_be_low_latency(false),
-      background_frame_interval(base::TimeDelta::FromSecondsD(
-          1.0 / settings.background_animation_rate)) {
-}
-
 SchedulerSettings::~SchedulerSettings() {}
 
 scoped_refptr<base::trace_event::ConvertableToTraceFormat>
diff --git a/cc/scheduler/scheduler_settings.h b/cc/scheduler/scheduler_settings.h
index 85f64e1..421409a 100644
--- a/cc/scheduler/scheduler_settings.h
+++ b/cc/scheduler/scheduler_settings.h
@@ -17,12 +17,10 @@
 }
 
 namespace cc {
-class LayerTreeSettings;
 
 class CC_EXPORT SchedulerSettings {
  public:
   SchedulerSettings();
-  explicit SchedulerSettings(const LayerTreeSettings& settings);
   ~SchedulerSettings();
 
   bool use_external_begin_frame_source;
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 189387c..c512987 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -26,6 +26,10 @@
       last_frame_number_swap_performed_(-1),
       last_frame_number_swap_requested_(-1),
       last_frame_number_begin_main_frame_sent_(-1),
+      animate_funnel_(false),
+      perform_swap_funnel_(false),
+      request_swap_funnel_(false),
+      send_begin_main_frame_funnel_(false),
       prepare_tiles_funnel_(0),
       consecutive_checkerboard_animations_(0),
       max_pending_swaps_(1),
@@ -41,14 +45,14 @@
       has_pending_tree_(false),
       pending_tree_is_ready_for_activation_(false),
       active_tree_needs_first_draw_(false),
-      did_commit_after_animating_(false),
       did_create_and_initialize_first_output_surface_(false),
       impl_latency_takes_priority_(false),
       skip_next_begin_main_frame_to_reduce_latency_(false),
       skip_begin_main_frame_to_reduce_latency_(false),
       continuous_painting_(false),
       children_need_begin_frames_(false),
-      defer_commits_(false) {
+      defer_commits_(false),
+      last_commit_had_no_updates_(false) {
 }
 
 const char* SchedulerStateMachine::OutputSurfaceStateToString(
@@ -151,12 +155,12 @@
 SchedulerStateMachine::AsValue() const {
   scoped_refptr<base::trace_event::TracedValue> state =
       new base::trace_event::TracedValue();
-  AsValueInto(state.get(), gfx::FrameTime::Now());
+  AsValueInto(state.get());
   return state;
 }
 
-void SchedulerStateMachine::AsValueInto(base::trace_event::TracedValue* state,
-                                        base::TimeTicks now) const {
+void SchedulerStateMachine::AsValueInto(
+    base::trace_event::TracedValue* state) const {
   state->BeginDictionary("major_state");
   state->SetString("next_action", ActionToString(NextAction()));
   state->SetString("begin_impl_frame_state",
@@ -168,35 +172,9 @@
                    ForcedRedrawOnTimeoutStateToString(forced_redraw_state_));
   state->EndDictionary();
 
-  state->BeginDictionary("major_timestamps_in_ms");
-  state->SetDouble("0_interval",
-                   begin_impl_frame_args_.interval.InMicroseconds() / 1000.0L);
-  state->SetDouble(
-      "1_now_to_deadline",
-      (begin_impl_frame_args_.deadline - now).InMicroseconds() / 1000.0L);
-  state->SetDouble(
-      "2_frame_time_to_now",
-      (now - begin_impl_frame_args_.frame_time).InMicroseconds() / 1000.0L);
-  state->SetDouble("3_frame_time_to_deadline",
-                   (begin_impl_frame_args_.deadline -
-                    begin_impl_frame_args_.frame_time).InMicroseconds() /
-                       1000.0L);
-  state->SetDouble("4_now",
-                   (now - base::TimeTicks()).InMicroseconds() / 1000.0L);
-  state->SetDouble(
-      "5_frame_time",
-      (begin_impl_frame_args_.frame_time - base::TimeTicks()).InMicroseconds() /
-          1000.0L);
-  state->SetDouble(
-      "6_deadline",
-      (begin_impl_frame_args_.deadline - base::TimeTicks()).InMicroseconds() /
-          1000.0L);
-  state->EndDictionary();
-
   state->BeginDictionary("minor_state");
   state->SetInteger("commit_count", commit_count_);
   state->SetInteger("current_frame_number", current_frame_number_);
-
   state->SetInteger("last_frame_number_animate_performed",
                     last_frame_number_animate_performed_);
   state->SetInteger("last_frame_number_swap_performed",
@@ -205,8 +183,12 @@
                     last_frame_number_swap_requested_);
   state->SetInteger("last_frame_number_begin_main_frame_sent",
                     last_frame_number_begin_main_frame_sent_);
-
-  state->SetInteger("prepare_tiles_funnel", prepare_tiles_funnel_);
+  state->SetBoolean("funnel: animate_funnel", animate_funnel_);
+  state->SetBoolean("funnel: perform_swap_funnel", perform_swap_funnel_);
+  state->SetBoolean("funnel: request_swap_funnel", request_swap_funnel_);
+  state->SetBoolean("funnel: send_begin_main_frame_funnel",
+                    send_begin_main_frame_funnel_);
+  state->SetInteger("funnel: prepare_tiles_funnel", prepare_tiles_funnel_);
   state->SetInteger("consecutive_checkerboard_animations",
                     consecutive_checkerboard_animations_);
   state->SetInteger("max_pending_swaps_", max_pending_swaps_);
@@ -223,7 +205,6 @@
                     pending_tree_is_ready_for_activation_);
   state->SetBoolean("active_tree_needs_first_draw",
                     active_tree_needs_first_draw_);
-  state->SetBoolean("did_commit_after_animating", did_commit_after_animating_);
   state->SetBoolean("did_create_and_initialize_first_output_surface",
                     did_create_and_initialize_first_output_surface_);
   state->SetBoolean("impl_latency_takes_priority",
@@ -243,6 +224,11 @@
 void SchedulerStateMachine::AdvanceCurrentFrameNumber() {
   current_frame_number_++;
 
+  animate_funnel_ = false;
+  perform_swap_funnel_ = false;
+  request_swap_funnel_ = false;
+  send_begin_main_frame_funnel_ = false;
+
   // "Drain" the PrepareTiles funnel.
   if (prepare_tiles_funnel_ > 0)
     prepare_tiles_funnel_--;
@@ -252,23 +238,6 @@
   skip_next_begin_main_frame_to_reduce_latency_ = false;
 }
 
-bool SchedulerStateMachine::HasAnimatedThisFrame() const {
-  return last_frame_number_animate_performed_ == current_frame_number_;
-}
-
-bool SchedulerStateMachine::HasSentBeginMainFrameThisFrame() const {
-  return current_frame_number_ ==
-         last_frame_number_begin_main_frame_sent_;
-}
-
-bool SchedulerStateMachine::HasSwappedThisFrame() const {
-  return current_frame_number_ == last_frame_number_swap_performed_;
-}
-
-bool SchedulerStateMachine::HasRequestedSwapThisFrame() const {
-  return current_frame_number_ == last_frame_number_swap_requested_;
-}
-
 bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
   // These are all the cases where we normally cannot or do not want to draw
   // but, if needs_redraw_ is true and we do not draw to make forward progress,
@@ -341,19 +310,16 @@
   if (PendingDrawsShouldBeAborted())
     return active_tree_needs_first_draw_;
 
+  // Do not draw too many times in a single frame. It's okay that we don't check
+  // this before checking for aborted draws because aborted draws do not request
+  // a swap.
+  if (request_swap_funnel_)
+    return false;
+
   // Don't draw if we are waiting on the first commit after a surface.
   if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
     return false;
 
-  // If a commit has occurred after the animate call, we need to call animate
-  // again before we should draw.
-  if (did_commit_after_animating_)
-    return false;
-
-  // After this line, we only want to send a swap request once per frame.
-  if (HasRequestedSwapThisFrame())
-    return false;
-
   // Do not queue too many swaps.
   if (pending_swaps_ >= max_pending_swaps_)
     return false;
@@ -390,12 +356,12 @@
 }
 
 bool SchedulerStateMachine::ShouldAnimate() const {
-  // Don't animate if we are waiting on the first commit after a surface.
-  if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
+  // Do not animate too many times in a single frame.
+  if (animate_funnel_)
     return false;
 
-  // If a commit occurred after our last call, we need to do animation again.
-  if (HasAnimatedThisFrame() && !did_commit_after_animating_)
+  // Don't animate if we are waiting on the first commit after a surface.
+  if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
     return false;
 
   if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING &&
@@ -406,6 +372,10 @@
 }
 
 bool SchedulerStateMachine::CouldSendBeginMainFrame() const {
+  // Do not send begin main frame too many times in a single frame.
+  if (send_begin_main_frame_funnel_)
+    return false;
+
   if (!needs_commit_)
     return false;
 
@@ -449,10 +419,6 @@
   if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT)
     return true;
 
-  // After this point, we only start a commit once per frame.
-  if (HasSentBeginMainFrameThisFrame())
-    return false;
-
   // We shouldn't normally accept commits if there isn't an OutputSurface.
   if (!HasInitializedOutputSurface())
     return false;
@@ -461,7 +427,7 @@
   // TODO(brianderson): Remove this restriction to improve throughput.
   bool just_swapped_in_deadline =
       begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
-      HasSwappedThisFrame();
+      perform_swap_funnel_;
   if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline)
     return false;
 
@@ -538,9 +504,10 @@
       return;
 
     case ACTION_ANIMATE:
+      DCHECK(!animate_funnel_);
       last_frame_number_animate_performed_ = current_frame_number_;
+      animate_funnel_ = true;
       needs_animate_ = false;
-      did_commit_after_animating_ = false;
       // TODO(skyostil): Instead of assuming this, require the client to tell
       // us.
       SetNeedsRedraw();
@@ -550,8 +517,10 @@
       DCHECK(!has_pending_tree_ ||
              settings_.main_frame_before_activation_enabled);
       DCHECK(visible_);
+      DCHECK(!send_begin_main_frame_funnel_);
       commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT;
       needs_commit_ = false;
+      send_begin_main_frame_funnel_ = true;
       last_frame_number_begin_main_frame_sent_ =
           current_frame_number_;
       return;
@@ -596,8 +565,8 @@
 void SchedulerStateMachine::UpdateStateOnCommit(bool commit_has_no_updates) {
   commit_count_++;
 
-  if (!commit_has_no_updates && HasAnimatedThisFrame())
-    did_commit_after_animating_ = true;
+  if (!commit_has_no_updates)
+    animate_funnel_ = false;
 
   if (commit_has_no_updates || settings_.main_frame_before_activation_enabled) {
     commit_state_ = COMMIT_STATE_IDLE;
@@ -645,6 +614,7 @@
 
   if (continuous_painting_)
     needs_commit_ = true;
+  last_commit_had_no_updates_ = commit_has_no_updates;
 }
 
 void SchedulerStateMachine::UpdateStateOnActivation() {
@@ -676,8 +646,11 @@
   needs_redraw_ = false;
   active_tree_needs_first_draw_ = false;
 
-  if (did_request_swap)
+  if (did_request_swap) {
+    DCHECK(!request_swap_funnel_);
+    request_swap_funnel_ = true;
     last_frame_number_swap_requested_ = current_frame_number_;
+  }
 }
 
 void SchedulerStateMachine::UpdateStateOnPrepareTiles() {
@@ -720,29 +693,6 @@
   return BeginFrameNeededToAnimateOrDraw();
 }
 
-bool SchedulerStateMachine::ShouldSetNeedsBeginFrames(
-    bool frame_source_needs_begin_frames) const {
-  bool needs_begin_frame = BeginFrameNeeded();
-
-  // Never call SetNeedsBeginFrames if the frame source has the right value.
-  if (needs_begin_frame == frame_source_needs_begin_frames)
-    return false;
-
-  // Always request the BeginFrame immediately if it's needed.
-  if (needs_begin_frame)
-    return true;
-
-  // Stop requesting BeginFrames after a deadline.
-  if (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE)
-    return true;
-
-  // Stop requesting BeginFrames immediately when output surface is lost.
-  if (!HasInitializedOutputSurface())
-    return true;
-
-  return false;
-}
-
 bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const {
   // ShouldPollForAnticipatedDrawTriggers is what we use in place of
   // ProactiveBeginFrameWanted when we are using the synchronous
@@ -815,18 +765,23 @@
   // SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame
   // provider and get sampled at an inopportune time, delaying the next
   // BeginImplFrame.
-  if (HasRequestedSwapThisFrame())
+  if (request_swap_funnel_)
+    return true;
+
+  // If the last commit was aborted because of early out (no updates), we should
+  // still want a begin frame in case there is a commit coming again.
+  if (last_commit_had_no_updates_)
     return true;
 
   return false;
 }
 
-void SchedulerStateMachine::OnBeginImplFrame(const BeginFrameArgs& args) {
+void SchedulerStateMachine::OnBeginImplFrame() {
   AdvanceCurrentFrameNumber();
-  begin_impl_frame_args_ = args;
   DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_IDLE)
       << AsValue()->ToString();
   begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING;
+  last_commit_had_no_updates_ = false;
 }
 
 void SchedulerStateMachine::OnBeginImplFrameDeadlinePending() {
@@ -910,7 +865,7 @@
 
   // If we just sent a BeginMainFrame and haven't hit the deadline yet, the main
   // thread is in a low latency mode.
-  if (HasSentBeginMainFrameThisFrame() &&
+  if (send_begin_main_frame_funnel_ &&
       (begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING ||
        begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME))
     return false;
@@ -934,8 +889,8 @@
     // Even if there's a new active tree to draw at the deadline or we've just
     // swapped it, it may have been triggered by a previous BeginImplFrame, in
     // which case the main thread is in a high latency mode.
-    return (active_tree_needs_first_draw_ || HasSwappedThisFrame()) &&
-           !HasSentBeginMainFrameThisFrame();
+    return (active_tree_needs_first_draw_ || perform_swap_funnel_) &&
+           !send_begin_main_frame_funnel_;
   }
 
   // If the active tree needs its first draw in any other state, we know the
@@ -976,7 +931,9 @@
 void SchedulerStateMachine::DidSwapBuffers() {
   pending_swaps_++;
   DCHECK_LE(pending_swaps_, max_pending_swaps_);
+  DCHECK(!perform_swap_funnel_);
 
+  perform_swap_funnel_ = true;
   last_frame_number_swap_performed_ = current_frame_number_;
 }
 
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index a411022..3914039 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -9,7 +9,6 @@
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time/time.h"
 #include "cc/base/cc_export.h"
 #include "cc/output/begin_frame_args.h"
 #include "cc/scheduler/commit_earlyout_reason.h"
@@ -115,8 +114,7 @@
   static const char* ActionToString(Action action);
 
   scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue() const;
-  void AsValueInto(base::trace_event::TracedValue* dict,
-                   base::TimeTicks now) const;
+  void AsValueInto(base::trace_event::TracedValue* dict) const;
 
   Action NextAction() const;
   void UpdateState(Action action);
@@ -125,10 +123,6 @@
   // to make progress.
   bool BeginFrameNeeded() const;
 
-  // Indicates whether the scheduler should call
-  // SetNeedsBeginFrames(BeginFrameNeeded()) on the frame source.
-  bool ShouldSetNeedsBeginFrames(bool frame_source_needs_begin_frames) const;
-
   // Indicates that we need to independently poll for new state and actions
   // because we can't expect a BeginImplFrame. This is mostly used to avoid
   // drawing repeat frames with the synchronous compositor without dropping
@@ -138,7 +132,7 @@
   // Indicates that the system has entered and left a BeginImplFrame callback.
   // The scheduler will not draw more than once in a given BeginImplFrame
   // callback nor send more than one BeginMainFrame message.
-  void OnBeginImplFrame(const BeginFrameArgs& args);
+  void OnBeginImplFrame();
   void OnBeginImplFrameDeadlinePending();
   void OnBeginImplFrameDeadline();
   void OnBeginImplFrameIdle();
@@ -284,10 +278,6 @@
   bool ShouldPrepareTiles() const;
 
   void AdvanceCurrentFrameNumber();
-  bool HasAnimatedThisFrame() const;
-  bool HasSentBeginMainFrameThisFrame() const;
-  bool HasRequestedSwapThisFrame() const;
-  bool HasSwappedThisFrame() const;
 
   void UpdateStateOnCommit(bool commit_had_no_updates);
   void UpdateStateOnActivation();
@@ -301,8 +291,7 @@
   CommitState commit_state_;
   ForcedRedrawOnTimeoutState forced_redraw_state_;
 
-  BeginFrameArgs begin_impl_frame_args_;
-
+  // These are used for tracing only.
   int commit_count_;
   int current_frame_number_;
   int last_frame_number_animate_performed_;
@@ -310,11 +299,18 @@
   int last_frame_number_swap_requested_;
   int last_frame_number_begin_main_frame_sent_;
 
+  // These are used to ensure that an action only happens once per frame,
+  // deadline, etc.
+  bool animate_funnel_;
+  bool perform_swap_funnel_;
+  bool request_swap_funnel_;
+  bool send_begin_main_frame_funnel_;
   // prepare_tiles_funnel_ is "filled" each time PrepareTiles is called
   // and "drained" on each BeginImplFrame. If the funnel gets too full,
   // we start throttling ACTION_PREPARE_TILES such that we average one
   // PrepareTiles per BeginImplFrame.
   int prepare_tiles_funnel_;
+
   int consecutive_checkerboard_animations_;
   int max_pending_swaps_;
   int pending_swaps_;
@@ -329,7 +325,6 @@
   bool has_pending_tree_;
   bool pending_tree_is_ready_for_activation_;
   bool active_tree_needs_first_draw_;
-  bool did_commit_after_animating_;
   bool did_create_and_initialize_first_output_surface_;
   bool impl_latency_takes_priority_;
   bool skip_next_begin_main_frame_to_reduce_latency_;
@@ -337,6 +332,7 @@
   bool continuous_painting_;
   bool children_need_begin_frames_;
   bool defer_commits_;
+  bool last_commit_had_no_updates_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine);
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index 98d5b18..4c2c267 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -132,6 +132,8 @@
   }
 
   using SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately;
+  using SchedulerStateMachine::ProactiveBeginFrameWanted;
+  using SchedulerStateMachine::UpdateStateOnCommit;
 };
 
 TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
@@ -152,8 +154,7 @@
 
     EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
     EXPECT_FALSE(state.BeginFrameNeeded());
-    state.OnBeginImplFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+    state.OnBeginImplFrame();
 
     EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
     state.OnBeginImplFrameDeadline();
@@ -171,8 +172,7 @@
 
     EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
     EXPECT_FALSE(state.BeginFrameNeeded());
-    state.OnBeginImplFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+    state.OnBeginImplFrame();
     EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
     state.OnBeginImplFrameDeadline();
   }
@@ -190,8 +190,7 @@
 
     EXPECT_TRUE(state.BeginFrameNeeded());
 
-    state.OnBeginImplFrame(
-        CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+    state.OnBeginImplFrame();
     EXPECT_ACTION_UPDATE_STATE(
         SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   }
@@ -224,7 +223,7 @@
   EXPECT_TRUE(state.BeginFrameNeeded());
 
   // Commit to the pending tree.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -240,7 +239,7 @@
 
   // Verify that the next commit starts while there is still a pending tree.
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -277,7 +276,7 @@
   state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.RedrawPending());
   EXPECT_TRUE(state.BeginFrameNeeded());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
@@ -294,7 +293,7 @@
 
   // Failing the draw makes us require a commit.
   state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -310,7 +309,7 @@
   EXPECT_TRUE(state.RedrawPending());
   EXPECT_TRUE(state.BeginFrameNeeded());
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
@@ -324,7 +323,7 @@
 
   // Missing high res content requires a commit (but not a redraw)
   state.DidDrawIfPossibleCompleted(DRAW_ABORTED_MISSING_HIGH_RES_CONTENT);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_FALSE(state.RedrawPending());
@@ -339,7 +338,7 @@
   state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.RedrawPending());
   EXPECT_TRUE(state.BeginFrameNeeded());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
@@ -360,7 +359,7 @@
 
   // Failing the draw for animation checkerboards makes us require a commit.
   state.DidDrawIfPossibleCompleted(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -377,7 +376,7 @@
   // Start a commit.
   state.SetNeedsCommit();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -407,7 +406,7 @@
   EXPECT_TRUE(state.RedrawPending());
 
   // The redraw should be forced at the end of the next BeginImplFrame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -431,7 +430,7 @@
   // Start a commit.
   state.SetNeedsCommit();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -484,7 +483,7 @@
   // Start a draw.
   state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.BeginFrameNeeded());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
@@ -501,7 +500,7 @@
 
   // We should not be trying to draw again now, but we have a commit pending.
   EXPECT_TRUE(state.BeginFrameNeeded());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
@@ -523,7 +522,7 @@
 
   // Draw the first frame.
   EXPECT_TRUE(state.BeginFrameNeeded());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
@@ -542,7 +541,7 @@
 
   // Move to another frame. This should now draw.
   EXPECT_TRUE(state.BeginFrameNeeded());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
 
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -677,8 +676,7 @@
       state.SetVisible(false);
       state.SetNeedsRedraw(true);
       if (j == 1)
-        state.OnBeginImplFrame(
-            CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+        state.OnBeginImplFrame();
 
       state.SetCanDraw(false);
       EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
@@ -700,7 +698,7 @@
   state.SetNeedsRedraw(true);
   state.SetVisible(true);
   state.SetCanDraw(false);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
   EXPECT_ACTION_UPDATE_STATE(
@@ -724,7 +722,7 @@
   EXPECT_TRUE(state.BeginFrameNeeded());
 
   // Begin the frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -758,7 +756,7 @@
   EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
   EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_IMPL_FRAME_STATE(
       SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING);
   EXPECT_ACTION(SchedulerStateMachine::ACTION_COMMIT);
@@ -791,7 +789,7 @@
   state.SetNeedsCommit();
 
   // Begin the frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -837,7 +835,7 @@
   state.SetNeedsCommit();
 
   // Begin the frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -867,7 +865,7 @@
 
   // Haven't draw since last commit, do not begin new main frame.
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
@@ -898,7 +896,7 @@
   state.SetNeedsCommit();
 
   // Begin the frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -929,7 +927,7 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   // Cannot BeginMainFrame yet since last commit is not yet activated and drawn.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_COMMIT_STATE(
       SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_ACTIVATION);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -950,7 +948,7 @@
   state.DidSwapBuffersComplete();
 
   // Haven't draw since last commit, do not begin new main frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
@@ -978,7 +976,7 @@
   state.SetNeedsCommit();
 
   // Begin the frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -1018,7 +1016,7 @@
   EXPECT_FALSE(state.needs_redraw());
 
   // Next BeginImplFrame should initiate second commit.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 }
@@ -1044,7 +1042,7 @@
   state.SetNeedsCommit();
 
   // Begin the frame while visible.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -1078,7 +1076,7 @@
   EXPECT_TRUE(state.NeedsCommit());
 
   // Start a new frame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 
@@ -1100,7 +1098,7 @@
 
   // Get into a begin frame / commit state.
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_COMMIT_STATE(
@@ -1129,7 +1127,7 @@
   EXPECT_FALSE(state.NeedsCommit());
   EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1137,7 +1135,7 @@
   // Verify another commit can start if requested, though.
   state.SetNeedsCommit();
   EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 }
 
@@ -1154,14 +1152,14 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   // Check that the first init does not SetNeedsCommit.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   // Check that a needs commit initiates a BeginMainFrame.
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 }
@@ -1185,7 +1183,7 @@
   state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
 
   // When the context is recreated, we should begin a commit.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 }
@@ -1209,14 +1207,14 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   // Once context recreation begins, nothing should happen.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   // While context is recreating, commits shouldn't begin.
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1232,7 +1230,7 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   // When the BeginFrame comes in we should begin a commit
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1258,7 +1256,7 @@
   // Finishing the first commit after initializing an output surface should
   // automatically cause a redraw.
   EXPECT_TRUE(state.RedrawPending());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
@@ -1268,7 +1266,7 @@
   EXPECT_FALSE(state.RedrawPending());
 
   // Next frame as no work to do.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1276,7 +1274,7 @@
   // Once the context is recreated, whether we draw should be based on
   // SetCanDraw if waiting on first draw after activate.
   state.SetNeedsRedraw(true);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   state.OnBeginImplFrameDeadline();
@@ -1292,7 +1290,7 @@
   // SetCanDraw if waiting on first draw after activate.
   state.SetNeedsRedraw(true);
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -1326,7 +1324,7 @@
 
   // Set damage and expect a draw.
   state.SetNeedsRedraw(true);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -1359,7 +1357,7 @@
   EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
   EXPECT_ACTION(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_IMPL_FRAME_STATE(
       SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING);
   EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE);
@@ -1387,7 +1385,7 @@
 
   // Set damage and expect a draw.
   state.SetNeedsRedraw(true);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -1420,7 +1418,7 @@
   EXPECT_IMPL_FRAME_STATE(SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
   EXPECT_ACTION(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_IMPL_FRAME_STATE(
       SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING);
   EXPECT_ACTION(SchedulerStateMachine::ACTION_NONE);
@@ -1441,7 +1439,7 @@
 
   // After we get a new output surface, the commit flow should start.
   state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1472,7 +1470,7 @@
   state.DidCreateAndInitializeOutputSurface();
 
   EXPECT_FALSE(state.RedrawPending());
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION(SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 }
 
@@ -1586,7 +1584,7 @@
 
   // This test mirrors what happens during the first frame of a scroll gesture.
   // First we get the input event and a BeginFrame.
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
 
   // As a response the compositor requests a redraw and a commit to tell the
   // main thread about the new scroll offset.
@@ -1621,7 +1619,7 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
@@ -1642,7 +1640,7 @@
   // in prefer impl latency mode.
   state.SetNeedsRedraw(true);
   state.SetNeedsCommit();
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -1679,7 +1677,7 @@
   // and did not just swap.
   state.SetNeedsCommit();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
   state.OnBeginImplFrameDeadline();
@@ -1694,7 +1692,7 @@
 
   state.SetNeedsCommit();
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
@@ -1717,7 +1715,7 @@
   EXPECT_TRUE(state.BeginFrameNeeded());
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
 
   state.OnBeginImplFrameDeadlinePending();
@@ -1739,7 +1737,7 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   EXPECT_TRUE(state.BeginFrameNeeded());
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -1763,7 +1761,7 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
   EXPECT_TRUE(state.BeginFrameNeeded());
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
@@ -1791,7 +1789,7 @@
   EXPECT_TRUE(state.BeginFrameNeeded());
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ANIMATE);
 
   state.SetNeedsAnimate();
@@ -1823,17 +1821,30 @@
   EXPECT_TRUE(state.BeginFrameNeeded());
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   state.OnBeginImplFrameDeadline();
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 
   state.SetDeferCommits(false);
-  state.OnBeginImplFrame(CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE));
+  state.OnBeginImplFrame();
   EXPECT_ACTION_UPDATE_STATE(
       SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
 }
 
+TEST(SchedulerStateMachineTest, EarlyOutCommitWantsProactiveBeginFrame) {
+  SchedulerSettings settings;
+  StateMachine state(settings);
+  SET_UP_STATE(state);
+
+  EXPECT_FALSE(state.ProactiveBeginFrameWanted());
+  bool commit_has_no_updates = true;
+  state.UpdateStateOnCommit(commit_has_no_updates);
+  EXPECT_TRUE(state.ProactiveBeginFrameWanted());
+  state.OnBeginImplFrame();
+  EXPECT_FALSE(state.ProactiveBeginFrameWanted());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 55f2164..b59359c 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -1752,9 +1752,8 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  // Do nothing when impl frame is in deadine pending state.
-  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
-  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+  // SetNeedsBeginFrames(false) is not called until the end of the frame.
+  EXPECT_NO_ACTION(client_);
 
   client_->Reset();
   scheduler_->NotifyBeginMainFrameStarted();
@@ -1762,8 +1761,10 @@
   EXPECT_ACTION("ScheduledActionCommit", client_, 0, 1);
 
   client_->Reset();
-  task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_);
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
 }
 
 void SchedulerTest::DidLoseOutputSurfaceAfterBeginFrameStartedWithHighLatency(
@@ -1785,19 +1786,20 @@
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
   // Do nothing when impl frame is in deadine pending state.
-  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
-  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+  EXPECT_NO_ACTION(client_);
 
   client_->Reset();
   // Run posted deadline.
   EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
   task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
-  // OnBeginImplFrameDeadline didn't schedule any actions because main frame is
-  // not yet completed.
-  EXPECT_NO_ACTION(client_);
+  // OnBeginImplFrameDeadline didn't schedule output surface creation because
+  // main frame is not yet completed.
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
 
   // BeginImplFrame is not started.
+  client_->Reset();
   task_runner().RunUntilTime(now_src()->Now() +
                              base::TimeDelta::FromMilliseconds(10));
   EXPECT_NO_ACTION(client_);
@@ -1851,19 +1853,19 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
+  // SetNeedsBeginFrames(false) is not called until the end of the frame.
   if (impl_side_painting) {
     // Sync tree should be forced to activate.
-    EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 3);
-    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
-    EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
+    EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_);
   } else {
-    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
-    EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+    EXPECT_NO_ACTION(client_);
   }
 
   client_->Reset();
-  task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_);
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
 }
 
 TEST_F(SchedulerTest, DidLoseOutputSurfaceAfterReadyToCommit) {
@@ -1890,13 +1892,15 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
-  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+  // SetNeedsBeginFrames(false) is not called until the end of the frame.
+  EXPECT_NO_ACTION(client_);
 
   client_->Reset();
-  task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 0, 2);
-  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 1, 2);
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+  EXPECT_ACTION("ScheduledActionPrepareTiles", client_, 0, 4);
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 1, 4);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 2, 4);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 3, 4);
 }
 
 TEST_F(SchedulerTest, DidLoseOutputSurfaceAfterBeginRetroFramePosted) {
@@ -2004,14 +2008,15 @@
   client_->Reset();
   EXPECT_FALSE(scheduler_->IsBeginRetroFrameArgsEmpty());
   scheduler_->DidLoseOutputSurface();
-  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
-  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+  EXPECT_NO_ACTION(client_);
   EXPECT_TRUE(scheduler_->IsBeginRetroFrameArgsEmpty());
 
   // BeginImplFrame deadline should abort drawing.
   client_->Reset();
-  task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_);
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
   EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
   EXPECT_FALSE(client_->needs_begin_frames());
 
@@ -2021,8 +2026,7 @@
   EXPECT_NO_ACTION(client_);
 }
 
-TEST_F(SchedulerTest,
-       StopBeginFrameAfterDidLoseOutputSurfaceWithSyntheticBeginFrameSource) {
+TEST_F(SchedulerTest, DidLoseOutputSurfaceWithSyntheticBeginFrameSource) {
   SetUpScheduler(true);
 
   // SetNeedsCommit should begin the frame on the next BeginImplFrame.
@@ -2031,7 +2035,7 @@
   EXPECT_TRUE(scheduler_->frame_source().NeedsBeginFrames());
 
   client_->Reset();
-  task_runner().RunPendingTasks();  // Run posted Tick.
+  AdvanceFrame();
   EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2);
   EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2);
   EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
@@ -2046,15 +2050,49 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  EXPECT_SINGLE_ACTION("SendBeginMainFrameNotExpectedSoon", client_);
-  EXPECT_FALSE(scheduler_->frame_source().NeedsBeginFrames());
+  // SetNeedsBeginFrames(false) is not called until the end of the frame.
+  EXPECT_NO_ACTION(client_);
+  EXPECT_TRUE(scheduler_->frame_source().NeedsBeginFrames());
 
   client_->Reset();
-  task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_);
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   EXPECT_FALSE(scheduler_->frame_source().NeedsBeginFrames());
 }
 
+TEST_F(SchedulerTest, DidLoseOutputSurfaceWhenIdle) {
+  scheduler_settings_.use_external_begin_frame_source = true;
+  SetUpScheduler(true);
+
+  // SetNeedsCommit should begin the frame.
+  scheduler_->SetNeedsCommit();
+  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2);
+  EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2);
+  EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted();
+  scheduler_->NotifyReadyToCommit();
+  EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_);
+
+  client_->Reset();
+  task_runner().RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+  EXPECT_ACTION("ScheduledActionAnimate", client_, 0, 2);
+  EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 1, 2);
+
+  // Idle time between BeginFrames.
+  client_->Reset();
+  scheduler_->DidLoseOutputSurface();
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
+}
+
 TEST_F(SchedulerTest, ScheduledActionActivateAfterBecomingInvisible) {
   scheduler_settings_.impl_side_painting = true;
   scheduler_settings_.use_external_begin_frame_source = true;
diff --git a/cc/surfaces/surface_display_output_surface.cc b/cc/surfaces/surface_display_output_surface.cc
index c07eece..0493724 100644
--- a/cc/surfaces/surface_display_output_surface.cc
+++ b/cc/surfaces/surface_display_output_surface.cc
@@ -25,6 +25,7 @@
       allocator_(allocator) {
   capabilities_.delegated_rendering = true;
   capabilities_.max_frames_pending = 1;
+  capabilities_.adjust_deadline_for_parent = true;
   capabilities_.can_force_reclaim_resources = true;
 }
 
diff --git a/cc/test/fake_layer_tree_host.cc b/cc/test/fake_layer_tree_host.cc
index fe0351d..143d22e 100644
--- a/cc/test/fake_layer_tree_host.cc
+++ b/cc/test/fake_layer_tree_host.cc
@@ -7,9 +7,9 @@
 namespace cc {
 FakeLayerTreeHost::FakeLayerTreeHost(FakeLayerTreeHostClient* client,
                                      const LayerTreeSettings& settings)
-    : LayerTreeHost(client, NULL, NULL, settings),
+    : LayerTreeHost(client, NULL, NULL, NULL, settings),
       client_(client),
-      host_impl_(settings, &proxy_, &manager_),
+      host_impl_(settings, &proxy_, &manager_, nullptr),
       needs_commit_(false) {
   client_->SetLayerTreeHost(this);
 }
diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc
index ccfb203..d026120 100644
--- a/cc/test/fake_layer_tree_host_impl.cc
+++ b/cc/test/fake_layer_tree_host_impl.cc
@@ -10,13 +10,15 @@
 namespace cc {
 
 FakeLayerTreeHostImpl::FakeLayerTreeHostImpl(Proxy* proxy,
-                                             SharedBitmapManager* manager)
+                                             SharedBitmapManager* manager,
+                                             TaskGraphRunner* task_graph_runner)
     : LayerTreeHostImpl(LayerTreeSettings(),
                         &client_,
                         proxy,
                         &stats_instrumentation_,
                         manager,
                         NULL,
+                        task_graph_runner,
                         0) {
   // Explicitly clear all debug settings.
   SetDebugState(LayerTreeDebugState());
@@ -30,13 +32,15 @@
 
 FakeLayerTreeHostImpl::FakeLayerTreeHostImpl(const LayerTreeSettings& settings,
                                              Proxy* proxy,
-                                             SharedBitmapManager* manager)
+                                             SharedBitmapManager* manager,
+                                             TaskGraphRunner* task_graph_runner)
     : LayerTreeHostImpl(settings,
                         &client_,
                         proxy,
                         &stats_instrumentation_,
                         manager,
                         NULL,
+                        task_graph_runner,
                         0) {
   // Explicitly clear all debug settings.
   SetDebugState(LayerTreeDebugState());
diff --git a/cc/test/fake_layer_tree_host_impl.h b/cc/test/fake_layer_tree_host_impl.h
index fe9bc4f..9c8f649 100644
--- a/cc/test/fake_layer_tree_host_impl.h
+++ b/cc/test/fake_layer_tree_host_impl.h
@@ -14,10 +14,13 @@
 
 class FakeLayerTreeHostImpl : public LayerTreeHostImpl {
  public:
-  FakeLayerTreeHostImpl(Proxy* proxy, SharedBitmapManager* manager);
+  FakeLayerTreeHostImpl(Proxy* proxy,
+                        SharedBitmapManager* manager,
+                        TaskGraphRunner* task_graph_runner);
   FakeLayerTreeHostImpl(const LayerTreeSettings& settings,
                         Proxy* proxy,
-                        SharedBitmapManager* manager);
+                        SharedBitmapManager* manager,
+                        TaskGraphRunner* task_graph_runner);
   ~FakeLayerTreeHostImpl() override;
 
   void ForcePrepareToDraw() {
@@ -36,6 +39,7 @@
 
   using LayerTreeHostImpl::ActivateSyncTree;
   using LayerTreeHostImpl::prepare_tiles_needed;
+  using LayerTreeHostImpl::is_likely_to_require_a_draw;
 
  private:
   BeginFrameArgs current_begin_frame_args_;
diff --git a/cc/test/fake_picture_layer_impl.cc b/cc/test/fake_picture_layer_impl.cc
index 5ac6f69..75f7fda 100644
--- a/cc/test/fake_picture_layer_impl.cc
+++ b/cc/test/fake_picture_layer_impl.cc
@@ -132,6 +132,7 @@
   DCHECK(layer_tree_impl()->IsPendingTree());
   Region invalidation_temp = invalidation;
   const PictureLayerTilingSet* pending_set = nullptr;
+  set_gpu_raster_max_texture_size(layer_tree_impl()->device_viewport_size());
   UpdateRasterSource(raster_source, &invalidation_temp, pending_set);
 }
 
diff --git a/cc/test/fake_picture_layer_impl.h b/cc/test/fake_picture_layer_impl.h
index 115fe55..ca29b9b 100644
--- a/cc/test/fake_picture_layer_impl.h
+++ b/cc/test/fake_picture_layer_impl.h
@@ -83,6 +83,10 @@
   using PictureLayerImpl::UpdateIdealScales;
   using PictureLayerImpl::MaximumTilingContentsScale;
 
+  void AddTilingUntilNextDraw(float scale) {
+    last_append_quads_tilings_.push_back(AddTiling(scale));
+  }
+
   float raster_page_scale() const { return raster_page_scale_; }
   void set_raster_page_scale(float scale) { raster_page_scale_ = scale; }
 
diff --git a/cc/test/fake_ui_resource_layer_tree_host_impl.cc b/cc/test/fake_ui_resource_layer_tree_host_impl.cc
index 9e7adde..43617a6 100644
--- a/cc/test/fake_ui_resource_layer_tree_host_impl.cc
+++ b/cc/test/fake_ui_resource_layer_tree_host_impl.cc
@@ -12,7 +12,9 @@
 FakeUIResourceLayerTreeHostImpl::FakeUIResourceLayerTreeHostImpl(
     Proxy* proxy,
     SharedBitmapManager* manager)
-    : FakeLayerTreeHostImpl(proxy, manager), fake_next_resource_id_(1) {}
+    : FakeLayerTreeHostImpl(proxy, manager, nullptr),
+      fake_next_resource_id_(1) {
+}
 
 FakeUIResourceLayerTreeHostImpl::~FakeUIResourceLayerTreeHostImpl() {}
 
diff --git a/cc/test/layer_tree_json_parser_unittest.cc b/cc/test/layer_tree_json_parser_unittest.cc
index a11d545..8286a0f 100644
--- a/cc/test/layer_tree_json_parser_unittest.cc
+++ b/cc/test/layer_tree_json_parser_unittest.cc
@@ -65,7 +65,7 @@
 TEST_F(LayerTreeJsonParserSanityCheck, Basic) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   LayerTreeImpl* tree = host_impl.active_tree();
 
   scoped_ptr<LayerImpl> root_impl(LayerImpl::Create(tree, 1));
@@ -94,7 +94,7 @@
 TEST_F(LayerTreeJsonParserSanityCheck, EventHandlerRegions) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   LayerTreeImpl* tree = host_impl.active_tree();
 
   scoped_ptr<LayerImpl> root_impl(LayerImpl::Create(tree, 1));
diff --git a/cc/test/layer_tree_pixel_resource_test.cc b/cc/test/layer_tree_pixel_resource_test.cc
index 6b6c00b..2e12953 100644
--- a/cc/test/layer_tree_pixel_resource_test.cc
+++ b/cc/test/layer_tree_pixel_resource_test.cc
@@ -171,8 +171,7 @@
                                draw_texture_target_);
 
       *tile_task_worker_pool = BitmapTileTaskWorkerPool::Create(
-          task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
-          resource_provider);
+          task_runner, task_graph_runner(), resource_provider);
       break;
     case GPU_TILE_TASK_WORKER_POOL:
       EXPECT_TRUE(context_provider);
@@ -182,7 +181,7 @@
                                draw_texture_target_);
 
       *tile_task_worker_pool = GpuTileTaskWorkerPool::Create(
-          task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
+          task_runner, task_graph_runner(),
           static_cast<GpuRasterizer*>(host_impl->rasterizer()));
       break;
     case ZERO_COPY_TILE_TASK_WORKER_POOL:
@@ -193,8 +192,7 @@
           ResourcePool::Create(resource_provider, draw_texture_target_);
 
       *tile_task_worker_pool = ZeroCopyTileTaskWorkerPool::Create(
-          task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
-          resource_provider);
+          task_runner, task_graph_runner(), resource_provider);
       break;
     case ONE_COPY_TILE_TASK_WORKER_POOL:
       EXPECT_TRUE(context_provider);
@@ -207,8 +205,8 @@
           ResourcePool::Create(resource_provider, draw_texture_target_);
 
       *tile_task_worker_pool = OneCopyTileTaskWorkerPool::Create(
-          task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
-          context_provider, resource_provider, staging_resource_pool->get());
+          task_runner, task_graph_runner(), context_provider, resource_provider,
+          staging_resource_pool->get());
       break;
     case PIXEL_BUFFER_TILE_TASK_WORKER_POOL:
       EXPECT_TRUE(context_provider);
@@ -217,8 +215,8 @@
           resource_provider, draw_texture_target_);
 
       *tile_task_worker_pool = PixelBufferTileTaskWorkerPool::Create(
-          task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
-          context_provider, resource_provider, max_transfer_buffer_usage_bytes);
+          task_runner, task_graph_runner(), context_provider, resource_provider,
+          max_transfer_buffer_usage_bytes);
       break;
   }
 }
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 3c6ff57..2fff95f 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -22,6 +22,7 @@
 #include "cc/test/test_context_provider.h"
 #include "cc/test/test_gpu_memory_buffer_manager.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/tiled_layer_test_common.h"
 #include "cc/trees/layer_tree_host_client.h"
 #include "cc/trees/layer_tree_host_impl.h"
@@ -209,15 +210,11 @@
       Proxy* proxy,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       RenderingStatsInstrumentation* stats_instrumentation) {
-    return make_scoped_ptr(
-        new LayerTreeHostImplForTesting(test_hooks,
-                                        settings,
-                                        host_impl_client,
-                                        proxy,
-                                        shared_bitmap_manager,
-                                        gpu_memory_buffer_manager,
-                                        stats_instrumentation));
+    return make_scoped_ptr(new LayerTreeHostImplForTesting(
+        test_hooks, settings, host_impl_client, proxy, shared_bitmap_manager,
+        gpu_memory_buffer_manager, task_graph_runner, stats_instrumentation));
   }
 
  protected:
@@ -228,6 +225,7 @@
       Proxy* proxy,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       RenderingStatsInstrumentation* stats_instrumentation)
       : LayerTreeHostImpl(settings,
                           host_impl_client,
@@ -235,6 +233,7 @@
                           stats_instrumentation,
                           shared_bitmap_manager,
                           gpu_memory_buffer_manager,
+                          task_graph_runner,
                           0),
         test_hooks_(test_hooks),
         block_notify_ready_to_activate_for_testing_(false),
@@ -455,12 +454,17 @@
   static scoped_ptr<LayerTreeHostForTesting> Create(
       TestHooks* test_hooks,
       LayerTreeHostClientForTesting* client,
+      SharedBitmapManager* shared_bitmap_manager,
+      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       const LayerTreeSettings& settings,
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
       scoped_ptr<BeginFrameSource> external_begin_frame_source) {
     scoped_ptr<LayerTreeHostForTesting> layer_tree_host(
-        new LayerTreeHostForTesting(test_hooks, client, settings));
+        new LayerTreeHostForTesting(test_hooks, client, shared_bitmap_manager,
+                                    gpu_memory_buffer_manager,
+                                    task_graph_runner, settings));
     if (impl_task_runner.get()) {
       layer_tree_host->InitializeForTesting(
           ThreadProxyForTest::Create(test_hooks,
@@ -483,12 +487,8 @@
   scoped_ptr<LayerTreeHostImpl> CreateLayerTreeHostImpl(
       LayerTreeHostImplClient* host_impl_client) override {
     return LayerTreeHostImplForTesting::Create(
-        test_hooks_,
-        settings(),
-        host_impl_client,
-        proxy(),
-        shared_bitmap_manager_.get(),
-        gpu_memory_buffer_manager_.get(),
+        test_hooks_, settings(), host_impl_client, proxy(),
+        shared_bitmap_manager_, gpu_memory_buffer_manager_, task_graph_runner_,
         rendering_stats_instrumentation());
   }
 
@@ -501,17 +501,23 @@
   void set_test_started(bool started) { test_started_ = started; }
 
  private:
-  LayerTreeHostForTesting(TestHooks* test_hooks,
-                          LayerTreeHostClient* client,
-                          const LayerTreeSettings& settings)
-      : LayerTreeHost(client, NULL, NULL, settings),
-        shared_bitmap_manager_(new TestSharedBitmapManager),
-        gpu_memory_buffer_manager_(new TestGpuMemoryBufferManager),
+  LayerTreeHostForTesting(
+      TestHooks* test_hooks,
+      LayerTreeHostClient* client,
+      SharedBitmapManager* shared_bitmap_manager,
+      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
+      const LayerTreeSettings& settings)
+      : LayerTreeHost(client, NULL, NULL, NULL, settings),
+        shared_bitmap_manager_(shared_bitmap_manager),
+        gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+        task_graph_runner_(task_graph_runner),
         test_hooks_(test_hooks),
         test_started_(false) {}
 
-  scoped_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
-  scoped_ptr<TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
+  SharedBitmapManager* shared_bitmap_manager_;
+  gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+  TaskGraphRunner* task_graph_runner_;
   TestHooks* test_hooks_;
   bool test_started_;
 };
@@ -663,9 +669,8 @@
 
   DCHECK(!impl_thread_ || impl_thread_->message_loop_proxy().get());
   layer_tree_host_ = LayerTreeHostForTesting::Create(
-      this,
-      client_.get(),
-      settings_,
+      this, client_.get(), shared_bitmap_manager_.get(),
+      gpu_memory_buffer_manager_.get(), task_graph_runner_.get(), settings_,
       base::MessageLoopProxy::current(),
       impl_thread_ ? impl_thread_->message_loop_proxy() : NULL,
       external_begin_frame_source.Pass());
@@ -793,6 +798,10 @@
 
   main_task_runner_ = base::MessageLoopProxy::current();
 
+  shared_bitmap_manager_.reset(new TestSharedBitmapManager);
+  gpu_memory_buffer_manager_.reset(new TestGpuMemoryBufferManager);
+  task_graph_runner_.reset(new TestTaskGraphRunner);
+
   delegating_renderer_ = delegating_renderer;
 
   // Spend less time waiting for BeginFrame because the output is
@@ -878,4 +887,13 @@
   layer_tree_host_ = nullptr;
 }
 
+LayerTreeHost* LayerTreeTest::layer_tree_host() {
+  // We check for a null proxy here as we sometimes ask for the layer tree host
+  // when the proxy does not exist, often for checking settings after a test has
+  // completed. For example, LTHPixelResourceTest::RunPixelResourceTest. See
+  // elsewhere in this file for other examples.
+  DCHECK(!proxy() || proxy()->IsMainThread() || proxy()->IsMainThreadBlocked());
+  return layer_tree_host_.get();
+}
+
 }  // namespace cc
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index c2b18b1..35a3546 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -21,6 +21,7 @@
 class LayerTreeHostClient;
 class LayerTreeHostImpl;
 class TestContextProvider;
+class TestGpuMemoryBufferManager;
 class TestWebGraphicsContext3D;
 
 // Used by test stubs to notify the test when something interesting happens.
@@ -195,10 +196,13 @@
   Proxy* proxy() const {
     return layer_tree_host_ ? layer_tree_host_->proxy() : NULL;
   }
+  TaskGraphRunner* task_graph_runner() const {
+    return task_graph_runner_.get();
+  }
 
   bool TestEnded() const { return ended_; }
 
-  LayerTreeHost* layer_tree_host() { return layer_tree_host_.get(); }
+  LayerTreeHost* layer_tree_host();
   bool delegating_renderer() const { return delegating_renderer_; }
   FakeOutputSurface* output_surface() { return output_surface_; }
   int LastCommittedSourceFrameNumber(LayerTreeHostImpl* impl) const;
@@ -234,6 +238,9 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_ptr<base::Thread> impl_thread_;
+  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
+  scoped_ptr<TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
+  scoped_ptr<TaskGraphRunner> task_graph_runner_;
   base::CancelableClosure timeout_;
   scoped_refptr<TestContextProvider> compositor_contexts_;
   base::WeakPtr<LayerTreeTest> main_thread_weak_ptr_;
diff --git a/cc/test/ordered_simple_task_runner.cc b/cc/test/ordered_simple_task_runner.cc
index e337909..9fc22ad 100644
--- a/cc/test/ordered_simple_task_runner.cc
+++ b/cc/test/ordered_simple_task_runner.cc
@@ -127,6 +127,10 @@
   return true;
 }
 
+size_t OrderedSimpleTaskRunner::NumPendingTasks() const {
+  return pending_tasks_.size();
+}
+
 bool OrderedSimpleTaskRunner::HasPendingTasks() const {
   return pending_tasks_.size() > 0;
 }
diff --git a/cc/test/ordered_simple_task_runner.h b/cc/test/ordered_simple_task_runner.h
index 54b1db4..6848303 100644
--- a/cc/test/ordered_simple_task_runner.h
+++ b/cc/test/ordered_simple_task_runner.h
@@ -73,6 +73,7 @@
     advance_now_ = advance_now;
   }
 
+  size_t NumPendingTasks() const;
   bool HasPendingTasks() const;
   base::TimeTicks NextTaskTime();
   base::TimeDelta DelayToNextTaskTime();
diff --git a/cc/test/test_now_source.cc b/cc/test/test_now_source.cc
index 0576d06..e1a633f 100644
--- a/cc/test/test_now_source.cc
+++ b/cc/test/test_now_source.cc
@@ -23,17 +23,21 @@
 }
 
 TestNowSource::TestNowSource()
-    : initial_(base::TimeTicks::FromInternalValue(10000)), now_() {
+    : initial_(base::TimeTicks::FromInternalValue(10000)),
+      now_(),
+      num_now_calls_(0) {
   Reset();
 }
 
 TestNowSource::TestNowSource(base::TimeTicks initial)
-    : initial_(initial), now_() {
+    : initial_(initial), now_(), num_now_calls_(0) {
   Reset();
 }
 
 TestNowSource::TestNowSource(int64_t initial)
-    : initial_(base::TimeTicks::FromInternalValue(initial)), now_() {
+    : initial_(base::TimeTicks::FromInternalValue(initial)),
+      now_(),
+      num_now_calls_(0) {
   Reset();
 }
 
@@ -53,6 +57,7 @@
 }
 
 base::TimeTicks TestNowSource::Now() const {
+  num_now_calls_++;
   return now_;
 }
 
diff --git a/cc/test/test_now_source.h b/cc/test/test_now_source.h
index 54e5a28..9b46bbe 100644
--- a/cc/test/test_now_source.h
+++ b/cc/test/test_now_source.h
@@ -37,6 +37,8 @@
   void AsValueInto(base::trace_event::TracedValue* state) const;
   std::string ToString() const;
 
+  int NumNowCalls() const { return num_now_calls_; }
+
  protected:
   TestNowSource();
   explicit TestNowSource(int64_t initial);
@@ -44,6 +46,7 @@
 
   base::TimeTicks initial_;
   base::TimeTicks now_;
+  mutable int num_now_calls_;
 
  private:
   friend class base::RefCounted<TestNowSource>;
diff --git a/cc/test/test_task_graph_runner.cc b/cc/test/test_task_graph_runner.cc
new file mode 100644
index 0000000..fe9734e
--- /dev/null
+++ b/cc/test/test_task_graph_runner.cc
@@ -0,0 +1,23 @@
+// 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/test/test_task_graph_runner.h"
+
+namespace cc {
+
+TestTaskGraphRunner::TestTaskGraphRunner()
+    : worker_thread_(this, "CompositorWorker") {
+  worker_thread_.Start();
+}
+
+TestTaskGraphRunner::~TestTaskGraphRunner() {
+  TaskGraphRunner::Shutdown();
+  worker_thread_.Join();
+}
+
+void TestTaskGraphRunner::Run() {
+  TaskGraphRunner::Run();
+}
+
+}  // namespace cc
diff --git a/cc/test/test_task_graph_runner.h b/cc/test/test_task_graph_runner.h
new file mode 100644
index 0000000..8314e11
--- /dev/null
+++ b/cc/test/test_task_graph_runner.h
@@ -0,0 +1,30 @@
+// 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 CC_TEST_TEST_TASK_GRAPH_RUNNER_H_
+#define CC_TEST_TEST_TASK_GRAPH_RUNNER_H_
+
+#include "base/threading/simple_thread.h"
+#include "cc/resources/task_graph_runner.h"
+
+namespace cc {
+
+class TestTaskGraphRunner : public TaskGraphRunner,
+                            public base::DelegateSimpleThread::Delegate {
+ public:
+  TestTaskGraphRunner();
+  ~TestTaskGraphRunner() override;
+
+  // Overridden from base::DelegateSimpleThread::Delegate:
+  void Run() override;
+
+ private:
+  base::DelegateSimpleThread worker_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestTaskGraphRunner);
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_TEST_TASK_GRAPH_RUNNER_H_
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index 866f09d..235dfcf 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -283,8 +283,7 @@
   RectMapData& data = RectDataForLayer(layer->id(), &layer_is_new);
   gfx::Rect old_rect_in_target_space = data.rect_;
 
-  gfx::Rect rect_in_target_space = MathUtil::MapEnclosingClippedRect(
-      layer->draw_transform(), gfx::Rect(layer->content_bounds()));
+  gfx::Rect rect_in_target_space = layer->GetEnclosingRectInTargetSpace();
   data.Update(rect_in_target_space, mailboxId_);
 
   gfx::RectF damage_rect =
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index 95609d3..2df71dd 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -12,6 +12,7 @@
 #include "cc/test/fake_layer_tree_host_impl.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_host_common.h"
 #include "cc/trees/single_thread_proxy.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -74,7 +75,8 @@
 
 class DamageTrackerTest : public testing::Test {
  public:
-  DamageTrackerTest() : host_impl_(&proxy_, &shared_bitmap_manager_) {}
+  DamageTrackerTest()
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) {}
 
   scoped_ptr<LayerImpl> CreateTestTreeWithOneSurface() {
     scoped_ptr<LayerImpl> root =
@@ -176,6 +178,7 @@
  protected:
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
 };
 
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 8a3e644..9d2be10 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -21,6 +21,7 @@
 #include "cc/animation/layer_animation_controller.h"
 #include "cc/base/math_util.h"
 #include "cc/debug/devtools_instrumentation.h"
+#include "cc/debug/frame_viewer_instrumentation.h"
 #include "cc/debug/rendering_stats_instrumentation.h"
 #include "cc/input/layer_selection_bound.h"
 #include "cc/input/page_scale_animation.h"
@@ -55,6 +56,7 @@
     LayerTreeHostClient* client,
     SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+    TaskGraphRunner* task_graph_runner,
     const LayerTreeSettings& settings,
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
@@ -62,7 +64,8 @@
   DCHECK(main_task_runner.get());
   DCHECK(impl_task_runner.get());
   scoped_ptr<LayerTreeHost> layer_tree_host(new LayerTreeHost(
-      client, shared_bitmap_manager, gpu_memory_buffer_manager, settings));
+      client, shared_bitmap_manager, gpu_memory_buffer_manager,
+      task_graph_runner, settings));
   layer_tree_host->InitializeThreaded(main_task_runner,
                                       impl_task_runner,
                                       external_begin_frame_source.Pass());
@@ -74,11 +77,13 @@
     LayerTreeHostSingleThreadClient* single_thread_client,
     SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+    TaskGraphRunner* task_graph_runner,
     const LayerTreeSettings& settings,
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     scoped_ptr<BeginFrameSource> external_begin_frame_source) {
   scoped_ptr<LayerTreeHost> layer_tree_host(new LayerTreeHost(
-      client, shared_bitmap_manager, gpu_memory_buffer_manager, settings));
+      client, shared_bitmap_manager, gpu_memory_buffer_manager,
+      task_graph_runner, settings));
   layer_tree_host->InitializeSingleThreaded(single_thread_client,
                                             main_task_runner,
                                             external_begin_frame_source.Pass());
@@ -89,6 +94,7 @@
     LayerTreeHostClient* client,
     SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+    TaskGraphRunner* task_graph_runner,
     const LayerTreeSettings& settings)
     : micro_benchmark_controller_(this),
       next_ui_resource_id_(1),
@@ -120,6 +126,7 @@
       next_commit_forces_redraw_(false),
       shared_bitmap_manager_(shared_bitmap_manager),
       gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+      task_graph_runner_(task_graph_runner),
       surface_id_namespace_(0u),
       next_surface_sequence_(1u) {
   if (settings_.accelerated_animation_enabled)
@@ -268,6 +275,15 @@
     contents_texture_manager_->ReduceMemory(host_impl->resource_provider());
   }
 
+  bool is_new_trace;
+  TRACE_EVENT_IS_NEW_TRACE(&is_new_trace);
+  if (is_new_trace &&
+      frame_viewer_instrumentation::IsTracingLayerTreeSnapshots() &&
+      root_layer()) {
+    LayerTreeHostCommon::CallFunctionForSubtree(
+        root_layer(), [](Layer* layer) { layer->DidBeginTracing(); });
+  }
+
   LayerTreeImpl* sync_tree = host_impl->sync_tree();
 
   if (next_commit_forces_redraw_) {
@@ -420,17 +436,14 @@
 scoped_ptr<LayerTreeHostImpl> LayerTreeHost::CreateLayerTreeHostImpl(
     LayerTreeHostImplClient* client) {
   DCHECK(proxy_->IsImplThread());
-  scoped_ptr<LayerTreeHostImpl> host_impl =
-      LayerTreeHostImpl::Create(settings_,
-                                client,
-                                proxy_.get(),
-                                rendering_stats_instrumentation_.get(),
-                                shared_bitmap_manager_,
-                                gpu_memory_buffer_manager_,
-                                id_);
+  scoped_ptr<LayerTreeHostImpl> host_impl = LayerTreeHostImpl::Create(
+      settings_, client, proxy_.get(), rendering_stats_instrumentation_.get(),
+      shared_bitmap_manager_, gpu_memory_buffer_manager_, task_graph_runner_,
+      id_);
   host_impl->SetUseGpuRasterization(UseGpuRasterization());
   shared_bitmap_manager_ = NULL;
   gpu_memory_buffer_manager_ = NULL;
+  task_graph_runner_ = NULL;
   top_controls_manager_weak_ptr_ =
       host_impl->top_controls_manager()->AsWeakPtr();
   input_handler_weak_ptr_ = host_impl->AsWeakPtr();
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 1379d08..e1bcad5 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -61,6 +61,7 @@
 class ResourceProvider;
 class ResourceUpdateQueue;
 class SharedBitmapManager;
+class TaskGraphRunner;
 class TopControlsManager;
 class UIResourceRequest;
 struct PendingPageScaleAnimation;
@@ -75,6 +76,7 @@
       LayerTreeHostClient* client,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       const LayerTreeSettings& settings,
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
@@ -85,6 +87,7 @@
       LayerTreeHostSingleThreadClient* single_thread_client,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       const LayerTreeSettings& settings,
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
       scoped_ptr<BeginFrameSource> external_begin_frame_source);
@@ -307,6 +310,7 @@
   LayerTreeHost(LayerTreeHostClient* client,
                 SharedBitmapManager* shared_bitmap_manager,
                 gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+                TaskGraphRunner* task_graph_runner,
                 const LayerTreeSettings& settings);
   void InitializeThreaded(
       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
@@ -440,6 +444,7 @@
 
   SharedBitmapManager* shared_bitmap_manager_;
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+  TaskGraphRunner* task_graph_runner_;
 
   ScopedPtrVector<SwapPromise> swap_promise_list_;
   std::set<SwapPromiseMonitor*> swap_promise_monitor_;
diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h
index 88197da..404f407 100644
--- a/cc/trees/layer_tree_host_common.h
+++ b/cc/trees/layer_tree_host_common.h
@@ -150,7 +150,9 @@
 
   struct ScrollUpdateInfo {
     int layer_id;
-    gfx::Vector2dF scroll_delta;
+    // TODO(miletus): Use ScrollOffset once LayerTreeHost/Blink fully supports
+    // franctional scroll offset.
+    gfx::Vector2d scroll_delta;
   };
 };
 
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index dd797d5..082d14f 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -28,6 +28,7 @@
 #include "cc/test/fake_picture_layer_impl.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/layer_tree_host_common_test.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/proxy.h"
 #include "cc/trees/single_thread_proxy.h"
@@ -315,7 +316,7 @@
 
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
 
   gfx::Transform identity_matrix;
   scoped_ptr<LayerImpl> sublayer_scoped_ptr(
@@ -5721,7 +5722,7 @@
 TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.pending_tree(), 1);
 
@@ -5763,7 +5764,7 @@
       public testing::TestWithParam<LCDTextTestParam> {
  public:
   LCDTextTest()
-      : host_impl_(&proxy_, &shared_bitmap_manager_),
+      : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_),
         root_(nullptr),
         child_(nullptr),
         grand_child_(nullptr) {}
@@ -5810,6 +5811,7 @@
 
   FakeImplProxy proxy_;
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeLayerTreeHostImpl host_impl_;
 
   LayerImpl* root_;
@@ -5947,7 +5949,7 @@
 TEST_F(LayerTreeHostCommonTest, SubtreeHidden_SingleLayer) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6005,7 +6007,7 @@
 TEST_F(LayerTreeHostCommonTest, SubtreeHidden_SingleLayerImpl) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6050,7 +6052,7 @@
 TEST_F(LayerTreeHostCommonTest, SubtreeHidden_TwoLayers) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6107,7 +6109,7 @@
 TEST_F(LayerTreeHostCommonTest, SubtreeHidden_TwoLayersImpl) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6152,7 +6154,7 @@
 TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6299,7 +6301,7 @@
 TEST_F(LayerTreeHostCommonTest, ClippedOutCopyRequest) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6374,7 +6376,7 @@
 TEST_F(LayerTreeHostCommonTest, VisibleContentRectInsideSurface) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   const gfx::Transform identity_matrix;
 
@@ -6985,7 +6987,7 @@
 TEST_F(LayerTreeHostCommonTest, CanRenderToSeparateSurface) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   scoped_ptr<LayerImpl> root =
       LayerImpl::Create(host_impl.active_tree(), 12345);
   scoped_ptr<LayerImpl> child1 =
@@ -7646,6 +7648,55 @@
   EXPECT_TRUE(render_surface_layer_list.at(2)->render_surface());
 }
 
+TEST_F(LayerTreeHostCommonTest, FixedPositionWithInterveningRenderSurface) {
+  // Ensures that when we have a render surface between a fixed position layer
+  // and its container, we compute the fixed position layer's draw transform
+  // with respect to that intervening render surface, not with respect to its
+  // container's render target.
+  //
+  // + root
+  //   + render_surface
+  //     + fixed
+  //
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> render_surface =
+      make_scoped_refptr(new LayerWithForcedDrawsContent());
+  scoped_refptr<LayerWithForcedDrawsContent> fixed =
+      make_scoped_refptr(new LayerWithForcedDrawsContent());
+
+  root->AddChild(render_surface);
+  render_surface->AddChild(fixed);
+
+  root->SetIsContainerForFixedPositionLayers(true);
+  render_surface->SetForceRenderSurface(true);
+
+  LayerPositionConstraint constraint;
+  constraint.set_is_fixed_position(true);
+  fixed->SetPositionConstraint(constraint);
+
+  SetLayerPropertiesForTesting(root.get(), gfx::Transform(), gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(50, 50), true, false);
+  SetLayerPropertiesForTesting(render_surface.get(), gfx::Transform(),
+                               gfx::Point3F(), gfx::PointF(7.f, 9.f),
+                               gfx::Size(50, 50), true, false);
+  SetLayerPropertiesForTesting(fixed.get(), gfx::Transform(), gfx::Point3F(),
+                               gfx::PointF(10.f, 15.f), gfx::Size(50, 50), true,
+                               false);
+
+  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  gfx::Transform expected_draw_transform;
+  expected_draw_transform.Translate(10.f, 15.f);
+  EXPECT_EQ(expected_draw_transform, fixed->draw_transform());
+
+  gfx::Transform expected_screen_space_transform;
+  expected_screen_space_transform.Translate(17.f, 24.f);
+  EXPECT_EQ(expected_screen_space_transform, fixed->screen_space_transform());
+}
+
 TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) {
   // This test verifies that a scrolling layer that gets snapped to
   // integer coordinates doesn't move a fixed position child.
@@ -7657,7 +7708,7 @@
   //
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
   scoped_ptr<LayerImpl> container =
@@ -7801,7 +7852,7 @@
   //
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   host_impl.CreatePendingTree();
   scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
   scoped_ptr<LayerImpl> container =
@@ -7895,7 +7946,7 @@
 TEST_F(LayerTreeHostCommonTest, MaximumAnimationScaleFactor) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   gfx::Transform identity_matrix;
   scoped_ptr<AnimationScaleFactorTrackingLayerImpl> grand_parent =
       AnimationScaleFactorTrackingLayerImpl::Create(host_impl.active_tree(), 1);
@@ -8112,7 +8163,7 @@
 TEST_F(LayerTreeHostCommonTest, RenderSurfaceLayerListMembership) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
   gfx::Transform identity_matrix;
 
   scoped_ptr<LayerImpl> grand_parent =
@@ -8355,7 +8406,7 @@
 TEST_F(LayerTreeHostCommonTest, DrawPropertyScales) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
 
   scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
   LayerImpl* root_layer = root.get();
@@ -8635,7 +8686,7 @@
 TEST_F(LayerTreeHostCommonTest, BoundsDeltaAffectVisibleContentRect) {
   FakeImplProxy proxy;
   TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
+  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
 
   // Set two layers: the root layer clips it's child,
   // the child draws its content.
@@ -8854,5 +8905,43 @@
   ExecuteCalculateDrawProperties(root.get());
 }
 
+TEST_F(LayerTreeHostCommonTest, OnlyApplyFixedPositioningOnce) {
+  gfx::Transform identity;
+  gfx::Transform translate_z;
+  translate_z.Translate3d(0, 0, 10);
+
+  scoped_refptr<Layer> root = Layer::Create();
+  SetLayerPropertiesForTesting(root.get(), identity, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(800, 800), true, false);
+  root->SetIsContainerForFixedPositionLayers(true);
+
+  scoped_refptr<Layer> frame_clip = Layer::Create();
+  SetLayerPropertiesForTesting(frame_clip.get(), translate_z, gfx::Point3F(),
+                               gfx::PointF(500, 100), gfx::Size(100, 100), true,
+                               false);
+  frame_clip->SetMasksToBounds(true);
+
+  scoped_refptr<LayerWithForcedDrawsContent> fixed =
+      make_scoped_refptr(new LayerWithForcedDrawsContent());
+  SetLayerPropertiesForTesting(fixed.get(), identity, gfx::Point3F(),
+                               gfx::PointF(), gfx::Size(1000, 1000), true,
+                               false);
+
+  LayerPositionConstraint constraint;
+  constraint.set_is_fixed_position(true);
+  fixed->SetPositionConstraint(constraint);
+
+  root->AddChild(frame_clip);
+  frame_clip->AddChild(fixed);
+
+  scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  gfx::Rect expected(0, 0, 100, 100);
+  EXPECT_EQ(expected, fixed->visible_rect_from_property_trees());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a8d4294..2c13fcb 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <map>
 
 #include "base/basictypes.h"
 #include "base/containers/hash_tables.h"
@@ -24,6 +25,7 @@
 #include "cc/debug/debug_rect_history.h"
 #include "cc/debug/devtools_instrumentation.h"
 #include "cc/debug/frame_rate_counter.h"
+#include "cc/debug/frame_viewer_instrumentation.h"
 #include "cc/debug/paint_time_counter.h"
 #include "cc/debug/rendering_stats_instrumentation.h"
 #include "cc/debug/traced_value.h"
@@ -168,14 +170,11 @@
     RenderingStatsInstrumentation* rendering_stats_instrumentation,
     SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+    TaskGraphRunner* task_graph_runner,
     int id) {
-  return make_scoped_ptr(new LayerTreeHostImpl(settings,
-                                               client,
-                                               proxy,
-                                               rendering_stats_instrumentation,
-                                               shared_bitmap_manager,
-                                               gpu_memory_buffer_manager,
-                                               id));
+  return make_scoped_ptr(new LayerTreeHostImpl(
+      settings, client, proxy, rendering_stats_instrumentation,
+      shared_bitmap_manager, gpu_memory_buffer_manager, task_graph_runner, id));
 }
 
 LayerTreeHostImpl::LayerTreeHostImpl(
@@ -185,6 +184,7 @@
     RenderingStatsInstrumentation* rendering_stats_instrumentation,
     SharedBitmapManager* shared_bitmap_manager,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+    TaskGraphRunner* task_graph_runner,
     int id)
     : client_(client),
       proxy_(proxy),
@@ -223,6 +223,7 @@
       micro_benchmark_controller_(this),
       shared_bitmap_manager_(shared_bitmap_manager),
       gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+      task_graph_runner_(task_graph_runner),
       id_(id),
       requires_high_res_to_draw_(false),
       is_likely_to_require_a_draw_(false),
@@ -1528,12 +1529,8 @@
   {
     TRACE_EVENT0("cc", "DrawLayers.FrameViewerTracing");
     TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
-       TRACE_DISABLED_BY_DEFAULT("cc.debug") ","
-       TRACE_DISABLED_BY_DEFAULT("cc.debug.quads") ","
-       TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers"),
-       "cc::LayerTreeHostImpl",
-       id_,
-       AsValueWithFrame(frame));
+        frame_viewer_instrumentation::kCategoryLayerTree,
+        "cc::LayerTreeHostImpl", id_, AsValueWithFrame(frame));
   }
 
   const DrawMode draw_mode = GetDrawMode();
@@ -2034,8 +2031,7 @@
         ResourcePool::Create(resource_provider_.get(), GL_TEXTURE_2D);
 
     *tile_task_worker_pool = BitmapTileTaskWorkerPool::Create(
-        task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
-        resource_provider_.get());
+        task_runner, task_graph_runner_, resource_provider_.get());
     return;
   }
 
@@ -2044,7 +2040,7 @@
         ResourcePool::Create(resource_provider_.get(), GL_TEXTURE_2D);
 
     *tile_task_worker_pool = GpuTileTaskWorkerPool::Create(
-        task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
+        task_runner, task_graph_runner_,
         static_cast<GpuRasterizer*>(rasterizer_.get()));
     return;
   }
@@ -2068,7 +2064,7 @@
         single_thread_synchronous_task_graph_runner_.reset(new TaskGraphRunner);
         task_graph_runner = single_thread_synchronous_task_graph_runner_.get();
       } else {
-        task_graph_runner = TileTaskWorkerPool::GetTaskGraphRunner();
+        task_graph_runner = task_graph_runner_;
       }
 
       *tile_task_worker_pool = ZeroCopyTileTaskWorkerPool::Create(
@@ -2084,9 +2080,8 @@
           ResourcePool::Create(resource_provider_.get(), GL_TEXTURE_2D);
 
       *tile_task_worker_pool = OneCopyTileTaskWorkerPool::Create(
-          task_runner, TileTaskWorkerPool::GetTaskGraphRunner(),
-          context_provider, resource_provider_.get(),
-          staging_resource_pool_.get());
+          task_runner, task_graph_runner_, context_provider,
+          resource_provider_.get(), staging_resource_pool_.get());
       return;
     }
   }
@@ -2100,7 +2095,7 @@
       resource_provider_.get(), GL_TEXTURE_2D);
 
   *tile_task_worker_pool = PixelBufferTileTaskWorkerPool::Create(
-      task_runner, TileTaskWorkerPool::GetTaskGraphRunner(), context_provider,
+      task_runner, task_graph_runner_, context_provider,
       resource_provider_.get(),
       GetMaxTransferBufferUsageBytes(context_provider->ContextCapabilities(),
                                      settings_.renderer_settings.refresh_rate));
@@ -3013,7 +3008,7 @@
   if (!scroll_delta.IsZero()) {
     LayerTreeHostCommon::ScrollUpdateInfo scroll;
     scroll.layer_id = layer_impl->id();
-    scroll.scroll_delta = gfx::Vector2dF(scroll_delta.x(), scroll_delta.y());
+    scroll.scroll_delta = gfx::Vector2d(scroll_delta.x(), scroll_delta.y());
     scroll_info->scrolls.push_back(scroll);
   }
 
@@ -3247,19 +3242,18 @@
   MathUtil::AddToTracedValue("device_viewport_size", device_viewport_size_,
                              state);
 
-  std::set<const Tile*> tiles;
-  active_tree_->GetAllTilesForTracing(&tiles);
+  std::map<const Tile*, TilePriority> tile_map;
+  active_tree_->GetAllTilesAndPrioritiesForTracing(&tile_map);
   if (pending_tree_)
-    pending_tree_->GetAllTilesForTracing(&tiles);
+    pending_tree_->GetAllTilesAndPrioritiesForTracing(&tile_map);
 
   state->BeginArray("active_tiles");
-  for (std::set<const Tile*>::const_iterator it = tiles.begin();
-       it != tiles.end();
-       ++it) {
-    const Tile* tile = *it;
+  for (const auto& pair : tile_map) {
+    const Tile* tile = pair.first;
+    const TilePriority& priority = pair.second;
 
     state->BeginDictionary();
-    tile->AsValueInto(state);
+    tile->AsValueWithPriorityInto(priority, state);
     state->EndDictionary();
   }
   state->EndArray();
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index defe796..2efe2da 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -142,6 +142,7 @@
       RenderingStatsInstrumentation* rendering_stats_instrumentation,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       int id);
   ~LayerTreeHostImpl() override;
 
@@ -531,12 +532,16 @@
       RenderingStatsInstrumentation* rendering_stats_instrumentation,
       SharedBitmapManager* shared_bitmap_manager,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      TaskGraphRunner* task_graph_runner,
       int id);
 
-
   // Virtual for testing.
   virtual void AnimateLayers(base::TimeTicks monotonic_time);
 
+  bool is_likely_to_require_a_draw() const {
+    return is_likely_to_require_a_draw_;
+  }
+
   LayerTreeHostImplClient* client_;
   Proxy* proxy_;
 
@@ -731,6 +736,7 @@
 
   SharedBitmapManager* shared_bitmap_manager_;
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+  TaskGraphRunner* task_graph_runner_;
   int id_;
 
   std::set<SwapPromiseMonitor*> swap_promise_monitor_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index e8471e2..284cae0 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -52,6 +52,7 @@
 #include "cc/test/render_pass_test_common.h"
 #include "cc/test/test_gpu_memory_buffer_manager.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/test/test_web_graphics_context_3d.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/single_thread_proxy.h"
@@ -82,6 +83,7 @@
         always_main_thread_blocked_(&proxy_),
         shared_bitmap_manager_(new TestSharedBitmapManager),
         gpu_memory_buffer_manager_(new TestGpuMemoryBufferManager),
+        task_graph_runner_(new TestTaskGraphRunner),
         on_can_draw_state_changed_called_(false),
         did_notify_ready_to_activate_(false),
         did_request_commit_(false),
@@ -162,13 +164,10 @@
 
   virtual bool CreateHostImpl(const LayerTreeSettings& settings,
                               scoped_ptr<OutputSurface> output_surface) {
-    host_impl_ = LayerTreeHostImpl::Create(settings,
-                                           this,
-                                           &proxy_,
-                                           &stats_instrumentation_,
-                                           shared_bitmap_manager_.get(),
-                                           gpu_memory_buffer_manager_.get(),
-                                           0);
+    host_impl_ = LayerTreeHostImpl::Create(
+        settings, this, &proxy_, &stats_instrumentation_,
+        shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(),
+        task_graph_runner_.get(), 0);
     bool init = host_impl_->InitializeRenderer(output_surface.Pass());
     host_impl_->SetViewportSize(gfx::Size(10, 10));
     return init;
@@ -192,14 +191,13 @@
 
   static void ExpectContains(const ScrollAndScaleSet& scroll_info,
                              int id,
-                             const gfx::Vector2dF& scroll_delta) {
+                             const gfx::Vector2d& scroll_delta) {
     int times_encountered = 0;
 
     for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) {
       if (scroll_info.scrolls[i].layer_id != id)
         continue;
-      EXPECT_VECTOR2DF_NEAR(scroll_delta, scroll_info.scrolls[i].scroll_delta,
-                            1.0e-10);
+      EXPECT_VECTOR_EQ(scroll_delta, scroll_info.scrolls[i].scroll_delta);
       times_encountered++;
     }
 
@@ -391,6 +389,7 @@
 
   scoped_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
   scoped_ptr<TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
+  scoped_ptr<TestTaskGraphRunner> task_graph_runner_;
   scoped_ptr<LayerTreeHostImpl> host_impl_;
   FakeRenderingStatsInstrumentation stats_instrumentation_;
   bool on_can_draw_state_changed_called_;
@@ -1543,6 +1542,7 @@
                           rendering_stats_instrumentation,
                           manager,
                           NULL,
+                          NULL,
                           0) {}
 
   BeginFrameArgs CurrentBeginFrameArgs() const override {
@@ -3714,7 +3714,7 @@
   // The layer should have scrolled down in its local coordinates.
   scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
   ExpectContains(*scroll_info.get(), scroll_layer->id(),
-                 gfx::Vector2dF(0, gesture_scroll_delta.x()));
+                 gfx::Vector2d(0, gesture_scroll_delta.x()));
 
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
@@ -3773,7 +3773,7 @@
 
     // The child layer should have scrolled down in its local coordinates an
     // amount proportional to the angle between it and the input scroll delta.
-    gfx::Vector2dF expected_scroll_delta(
+    gfx::Vector2d expected_scroll_delta(
         0, gesture_scroll_delta.y() *
                std::cos(MathUtil::Deg2Rad(child_layer_angle)));
     scoped_ptr<ScrollAndScaleSet> scroll_info =
@@ -3795,7 +3795,7 @@
 
     // The child layer should have scrolled down in its local coordinates an
     // amount proportional to the angle between it and the input scroll delta.
-    gfx::Vector2dF expected_scroll_delta(
+    gfx::Vector2d expected_scroll_delta(
         0, -gesture_scroll_delta.x() *
                std::sin(MathUtil::Deg2Rad(child_layer_angle)));
     scoped_ptr<ScrollAndScaleSet> scroll_info =
@@ -3804,7 +3804,7 @@
 
     // The root scroll layer should have scrolled more, since the input scroll
     // delta was mostly orthogonal to the child layer's vertical scroll axis.
-    gfx::Vector2dF expected_root_scroll_delta(
+    gfx::Vector2d expected_root_scroll_delta(
         gesture_scroll_delta.x() *
             std::pow(std::cos(MathUtil::Deg2Rad(child_layer_angle)), 2),
         0);
@@ -5042,16 +5042,10 @@
   // that we can force partial swap enabled.
   LayerTreeSettings settings;
   settings.renderer_settings.partial_swap_enabled = true;
-  scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
-      new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHostImpl> layer_tree_host_impl =
-      LayerTreeHostImpl::Create(settings,
-                                this,
-                                &proxy_,
-                                &stats_instrumentation_,
-                                shared_bitmap_manager.get(),
-                                NULL,
-                                0);
+      LayerTreeHostImpl::Create(
+          settings, this, &proxy_, &stats_instrumentation_,
+          shared_bitmap_manager_.get(), NULL, task_graph_runner_.get(), 0);
   layer_tree_host_impl->InitializeRenderer(output_surface.Pass());
   layer_tree_host_impl->SetViewportSize(gfx::Size(500, 500));
 
@@ -5297,8 +5291,8 @@
   CreateHostImpl(settings, FakeOutputSurface::Create3d(context_owned.Pass()));
   SetupRootLayerImpl(FakeLayerWithQuads::Create(host_impl_->active_tree(), 1));
 
-  // The first frame is not a partially-swapped one.
-  harness.MustSetScissor(0, 0, 10, 10);
+  // The first frame is not a partially-swapped one. No scissor should be set.
+  harness.MustSetNoScissor();
   harness.MustDrawSolidQuad();
   {
     LayerTreeHostImpl::FrameData frame;
@@ -5339,7 +5333,7 @@
   LayerTreeSettings settings;
   settings.renderer_settings.partial_swap_enabled = partial_swap;
   scoped_ptr<LayerTreeHostImpl> my_host_impl = LayerTreeHostImpl::Create(
-      settings, client, proxy, stats_instrumentation, manager, NULL, 0);
+      settings, client, proxy, stats_instrumentation, manager, NULL, NULL, 0);
   my_host_impl->InitializeRenderer(output_surface.Pass());
   my_host_impl->SetViewportSize(gfx::Size(100, 100));
 
@@ -6643,13 +6637,10 @@
 // doesn't support memory management extensions.
 TEST_F(LayerTreeHostImplTest, DefaultMemoryAllocation) {
   LayerTreeSettings settings;
-  host_impl_ = LayerTreeHostImpl::Create(settings,
-                                         this,
-                                         &proxy_,
-                                         &stats_instrumentation_,
-                                         shared_bitmap_manager_.get(),
-                                         gpu_memory_buffer_manager_.get(),
-                                         0);
+  host_impl_ = LayerTreeHostImpl::Create(
+      settings, this, &proxy_, &stats_instrumentation_,
+      shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(),
+      task_graph_runner_.get(), 0);
 
   scoped_ptr<OutputSurface> output_surface(
       FakeOutputSurface::Create3d(TestWebGraphicsContext3D::Create()));
@@ -6690,7 +6681,7 @@
   LayerTreeSettings settings;
   settings.gpu_rasterization_enabled = true;
   host_impl_ = LayerTreeHostImpl::Create(
-      settings, this, &proxy_, &stats_instrumentation_, NULL, NULL, 0);
+      settings, this, &proxy_, &stats_instrumentation_, NULL, NULL, NULL, 0);
   host_impl_->SetUseGpuRasterization(true);
   host_impl_->SetVisible(true);
   host_impl_->SetMemoryPolicy(policy1);
@@ -6753,8 +6744,9 @@
     LayerTreeSettings settings;
     settings.impl_side_painting = true;
 
-    fake_host_impl_ = new FakeLayerTreeHostImpl(
-        settings, &proxy_, shared_bitmap_manager_.get());
+    fake_host_impl_ = new FakeLayerTreeHostImpl(settings, &proxy_,
+                                                shared_bitmap_manager_.get(),
+                                                task_graph_runner_.get());
     host_impl_.reset(fake_host_impl_);
     host_impl_->InitializeRenderer(CreateOutputSurface());
     host_impl_->SetViewportSize(gfx::Size(10, 10));
@@ -8128,7 +8120,10 @@
       gfx::Size(10, 10), gfx::Size(10, 10)));
   Region empty_invalidation;
   const PictureLayerTilingSet* null_tiling_set = nullptr;
+  layer->set_gpu_raster_max_texture_size(host_impl_->device_viewport_size());
   layer->UpdateRasterSource(pile, &empty_invalidation, null_tiling_set);
+  nondraw_layer->set_gpu_raster_max_texture_size(
+      host_impl_->device_viewport_size());
   nondraw_layer->UpdateRasterSource(pile, &empty_invalidation, null_tiling_set);
 
   layer->AddChild(nondraw_layer.Pass());
@@ -8246,7 +8241,10 @@
       gfx::Size(10, 10), gfx::Size(10, 10)));
   Region empty_invalidation;
   const PictureLayerTilingSet* null_tiling_set = nullptr;
+  layer->set_gpu_raster_max_texture_size(host_impl_->device_viewport_size());
   layer->UpdateRasterSource(pile, &empty_invalidation, null_tiling_set);
+  nondraw_layer->set_gpu_raster_max_texture_size(
+      host_impl_->device_viewport_size());
   nondraw_layer->UpdateRasterSource(pile, &empty_invalidation, null_tiling_set);
 
   layer->AddChild(nondraw_layer.Pass());
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 9416474..201af9a 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -68,7 +68,17 @@
 namespace cc {
 namespace {
 
-class LayerTreeHostTest : public LayerTreeTest {};
+class LayerTreeHostTest : public LayerTreeTest {
+ public:
+  LayerTreeHostTest() : contents_texture_manager_(nullptr) {}
+
+  void DidInitializeOutputSurface() override {
+    contents_texture_manager_ = layer_tree_host()->contents_texture_manager();
+  }
+
+ protected:
+  PrioritizedResourceManager* contents_texture_manager_;
+};
 
 // Test if the LTHI receives ReadyToActivate notifications from the TileManager
 // when no raster tasks get scheduled.
@@ -473,6 +483,86 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNeedsRedrawRect);
 
+// Ensure the texture size of the pending and active trees are identical when a
+// layer is not in the viewport and a resize happens on the viewport
+class LayerTreeHostTestGpuRasterDeviceSizeChanged : public LayerTreeHostTest {
+ public:
+  LayerTreeHostTestGpuRasterDeviceSizeChanged()
+      : num_draws_(0), bounds_(500, 64), invalid_rect_(10, 10, 20, 20) {}
+
+  void BeginTest() override {
+    client_.set_fill_with_nonsolid_color(true);
+    root_layer_ = FakePictureLayer::Create(&client_);
+    root_layer_->SetIsDrawable(true);
+    gfx::Transform transform;
+    // Translate the layer out of the viewport to force it to not update its
+    // tile size via PushProperties.
+    transform.Translate(10000.0, 10000.0);
+    root_layer_->SetTransform(transform);
+    root_layer_->SetBounds(bounds_);
+    layer_tree_host()->SetRootLayer(root_layer_);
+    layer_tree_host()->SetViewportSize(bounds_);
+
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void InitializeSettings(LayerTreeSettings* settings) override {
+    settings->gpu_rasterization_enabled = true;
+    settings->gpu_rasterization_forced = true;
+  }
+
+  void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+    // Perform 2 commits.
+    if (!num_draws_) {
+      PostSetNeedsRedrawRectToMainThread(invalid_rect_);
+    } else {
+      EndTest();
+    }
+    num_draws_++;
+  }
+
+  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
+    if (num_draws_ == 2) {
+      auto pending_tree = host_impl->pending_tree();
+      auto pending_layer_impl =
+          static_cast<FakePictureLayerImpl*>(pending_tree->root_layer());
+      EXPECT_NE(pending_layer_impl, nullptr);
+
+      auto active_tree = host_impl->pending_tree();
+      auto active_layer_impl =
+          static_cast<FakePictureLayerImpl*>(active_tree->root_layer());
+      EXPECT_NE(pending_layer_impl, nullptr);
+
+      auto active_tiling_set = active_layer_impl->picture_layer_tiling_set();
+      auto active_tiling = active_tiling_set->tiling_at(0);
+      auto pending_tiling_set = pending_layer_impl->picture_layer_tiling_set();
+      auto pending_tiling = pending_tiling_set->tiling_at(0);
+      EXPECT_EQ(
+          pending_tiling->TilingDataForTesting().max_texture_size().width(),
+          active_tiling->TilingDataForTesting().max_texture_size().width());
+    }
+  }
+
+  void DidCommitAndDrawFrame() override {
+    // On the second commit, resize the viewport.
+    if (num_draws_ == 1) {
+      layer_tree_host()->SetViewportSize(gfx::Size(400, 64));
+    }
+  }
+
+  void AfterTest() override {}
+
+ private:
+  int num_draws_;
+  const gfx::Size bounds_;
+  const gfx::Rect invalid_rect_;
+  FakeContentLayerClient client_;
+  scoped_refptr<FakePictureLayer> root_layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_IMPL_TEST_F(
+    LayerTreeHostTestGpuRasterDeviceSizeChanged);
+
 class LayerTreeHostTestNoExtraCommitFromInvalidate : public LayerTreeHostTest {
  public:
   void InitializeSettings(LayerTreeSettings* settings) override {
@@ -692,6 +782,13 @@
  public:
   LayerTreeHostTestUndrawnLayersDamageLater() {}
 
+  void InitializeSettings(LayerTreeSettings* settings) override {
+    // If we don't set the minimum contents scale, it's harder to verify whether
+    // the damage we get is correct. For other scale amounts, please see
+    // LayerTreeHostTestDamageWithScale.
+    settings->minimum_contents_scale = 1.f;
+  }
+
   void SetupTree() override {
     if (layer_tree_host()->settings().impl_side_painting)
       root_layer_ = FakePictureLayer::Create(&client_);
@@ -786,6 +883,114 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestUndrawnLayersDamageLater);
 
+// Tests that if a layer is not drawn because of some reason in the parent then
+// its damage is preserved until the next time it is drawn.
+class LayerTreeHostTestDamageWithScale : public LayerTreeHostTest {
+ public:
+  LayerTreeHostTestDamageWithScale() {}
+
+  void SetupTree() override {
+    client_.set_fill_with_nonsolid_color(true);
+
+    scoped_ptr<FakePicturePile> pile(
+        new FakePicturePile(ImplSidePaintingSettings().minimum_contents_scale,
+                            ImplSidePaintingSettings().default_tile_grid_size));
+    root_layer_ =
+        FakePictureLayer::CreateWithRecordingSource(&client_, pile.Pass());
+    root_layer_->SetBounds(gfx::Size(50, 50));
+
+    pile.reset(
+        new FakePicturePile(ImplSidePaintingSettings().minimum_contents_scale,
+                            ImplSidePaintingSettings().default_tile_grid_size));
+    child_layer_ =
+        FakePictureLayer::CreateWithRecordingSource(&client_, pile.Pass());
+    child_layer_->SetBounds(gfx::Size(25, 25));
+    child_layer_->SetIsDrawable(true);
+    child_layer_->SetContentsOpaque(true);
+    root_layer_->AddChild(child_layer_);
+
+    layer_tree_host()->SetRootLayer(root_layer_);
+    LayerTreeHostTest::SetupTree();
+  }
+
+  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+    // Force the layer to have a tiling at 1.3f scale. Note that if we simply
+    // add tiling, it will be gone by the time we draw because of aggressive
+    // cleanup. AddTilingUntilNextDraw ensures that it remains there during
+    // damage calculation.
+    FakePictureLayerImpl* child_layer_impl = static_cast<FakePictureLayerImpl*>(
+        host_impl->active_tree()->LayerById(child_layer_->id()));
+    child_layer_impl->AddTilingUntilNextDraw(1.3f);
+  }
+
+  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+  DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+                                   LayerTreeHostImpl::FrameData* frame_data,
+                                   DrawResult draw_result) override {
+    EXPECT_EQ(DRAW_SUCCESS, draw_result);
+
+    gfx::RectF root_damage_rect;
+    if (!frame_data->render_passes.empty())
+      root_damage_rect = frame_data->render_passes.back()->damage_rect;
+
+    // The first time, the whole view needs be drawn.
+    // Afterwards, just the opacity of surface_layer1 is changed a few times,
+    // and each damage should be the bounding box of it and its child. If this
+    // was working improperly, the damage might not include its childs bounding
+    // box.
+    switch (host_impl->active_tree()->source_frame_number()) {
+      case 0:
+        EXPECT_EQ(gfx::Rect(root_layer_->bounds()), root_damage_rect);
+        break;
+      case 1: {
+        FakePictureLayerImpl* child_layer_impl =
+            static_cast<FakePictureLayerImpl*>(
+                host_impl->active_tree()->LayerById(child_layer_->id()));
+        // We remove tilings pretty aggressively if they are not ideal. Add this
+        // back in so that we can compare
+        // child_layer_impl->GetEnclosingRectInTargetSpace to the damage.
+        child_layer_impl->AddTilingUntilNextDraw(1.3f);
+
+        EXPECT_EQ(gfx::Rect(26, 26), root_damage_rect);
+        EXPECT_EQ(child_layer_impl->GetEnclosingRectInTargetSpace(),
+                  root_damage_rect);
+        EXPECT_TRUE(child_layer_impl->GetEnclosingRectInTargetSpace().Contains(
+            gfx::Rect(child_layer_->bounds())));
+        break;
+      }
+      default:
+        NOTREACHED();
+    }
+
+    return draw_result;
+  }
+
+  void DidCommitAndDrawFrame() override {
+    switch (layer_tree_host()->source_frame_number()) {
+      case 1: {
+        // Test not owning the surface.
+        child_layer_->SetOpacity(0.5f);
+        break;
+      }
+      case 2:
+        EndTest();
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+
+  void AfterTest() override {}
+
+ private:
+  FakeContentLayerClient client_;
+  scoped_refptr<Layer> root_layer_;
+  scoped_refptr<Layer> child_layer_;
+};
+
+MULTI_THREAD_IMPL_TEST_F(LayerTreeHostTestDamageWithScale);
+
 // Tests that if a layer is not drawn because of some reason in the parent,
 // causing its content bounds to not be computed, then when it is later drawn,
 // its content bounds get pushed.
@@ -1346,7 +1551,7 @@
   }
 
   void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
-    ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates);
+    ASSERT_EQ(0u, impl->settings().max_partial_texture_updates);
 
     TestWebGraphicsContext3D* context = TestContext();
 
@@ -1425,7 +1630,7 @@
     : public LayerTreeHostTestDirectRendererAtomicCommit {
  public:
   void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
-    ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates);
+    ASSERT_EQ(0u, impl->settings().max_partial_texture_updates);
 
     TestWebGraphicsContext3D* context = TestContext();
 
@@ -1542,7 +1747,7 @@
   }
 
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
-    ASSERT_EQ(1u, layer_tree_host()->settings().max_partial_texture_updates);
+    ASSERT_EQ(1u, impl->settings().max_partial_texture_updates);
 
     TestWebGraphicsContext3D* context = TestContext();
 
@@ -2082,7 +2287,7 @@
   LayerTreeHostWithProxy(FakeLayerTreeHostClient* client,
                          const LayerTreeSettings& settings,
                          scoped_ptr<FakeProxy> proxy)
-      : LayerTreeHost(client, NULL, NULL, settings) {
+      : LayerTreeHost(client, NULL, NULL, NULL, settings) {
     proxy->SetLayerTreeHost(this);
     client->SetLayerTreeHost(this);
     InitializeForTesting(proxy.Pass());
@@ -2154,14 +2359,9 @@
 
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
-  scoped_ptr<LayerTreeHost> host =
-      LayerTreeHost::CreateSingleThreaded(&client,
-                                          &client,
-                                          shared_bitmap_manager.get(),
-                                          NULL,
-                                          settings,
-                                          base::MessageLoopProxy::current(),
-                                          nullptr);
+  scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
+      &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
+      base::MessageLoopProxy::current(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2178,14 +2378,9 @@
 
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
-  scoped_ptr<LayerTreeHost> host =
-      LayerTreeHost::CreateSingleThreaded(&client,
-                                          &client,
-                                          shared_bitmap_manager.get(),
-                                          NULL,
-                                          settings,
-                                          base::MessageLoopProxy::current(),
-                                          nullptr);
+  scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
+      &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
+      base::MessageLoopProxy::current(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2202,14 +2397,9 @@
 
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
-  scoped_ptr<LayerTreeHost> host =
-      LayerTreeHost::CreateSingleThreaded(&client,
-                                          &client,
-                                          shared_bitmap_manager.get(),
-                                          NULL,
-                                          settings,
-                                          base::MessageLoopProxy::current(),
-                                          nullptr);
+  scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
+      &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
+      base::MessageLoopProxy::current(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2227,14 +2417,9 @@
 
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
-  scoped_ptr<LayerTreeHost> host =
-      LayerTreeHost::CreateSingleThreaded(&client,
-                                          &client,
-                                          shared_bitmap_manager.get(),
-                                          NULL,
-                                          settings,
-                                          base::MessageLoopProxy::current(),
-                                          nullptr);
+  scoped_ptr<LayerTreeHost> host = LayerTreeHost::CreateSingleThreaded(
+      &client, &client, shared_bitmap_manager.get(), NULL, NULL, settings,
+      base::MessageLoopProxy::current(), nullptr);
   client.SetLayerTreeHost(host.get());
   host->Composite(base::TimeTicks::Now());
 
@@ -2266,12 +2451,10 @@
                                bool visible) override {
     if (visible) {
       // One backing should remain unevicted.
-      EXPECT_EQ(
-          100u * 100u * 4u * 1u,
-          layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
+      EXPECT_EQ(100u * 100u * 4u * 1u,
+                contents_texture_manager_->MemoryUseBytes());
     } else {
-      EXPECT_EQ(
-          0u, layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
+      EXPECT_EQ(0u, contents_texture_manager_->MemoryUseBytes());
     }
 
     // Make sure that contents textures are marked as having been
@@ -2286,9 +2469,9 @@
     switch (num_commits_) {
       case 1:
         // All three backings should have memory.
-        EXPECT_EQ(
-            100u * 100u * 4u * 3u,
-            layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
+        EXPECT_EQ(100u * 100u * 4u * 3u,
+                  contents_texture_manager_->MemoryUseBytes());
+
         // Set a new policy that will kick out 1 of the 3 resources.
         // Because a resource was evicted, a commit will be kicked off.
         host_impl->SetMemoryPolicy(
@@ -2298,9 +2481,8 @@
         break;
       case 2:
         // Only two backings should have memory.
-        EXPECT_EQ(
-            100u * 100u * 4u * 2u,
-            layer_tree_host()->contents_texture_manager()->MemoryUseBytes());
+        EXPECT_EQ(100u * 100u * 4u * 2u,
+                  contents_texture_manager_->MemoryUseBytes());
         // Become backgrounded, which will cause 1 more resource to be
         // evicted.
         PostSetVisibleToMainThread(false);
@@ -3129,12 +3311,12 @@
   }
 
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
-    if (!layer_tree_host()->settings().impl_side_painting)
+    if (!impl->settings().impl_side_painting)
       PerformTest(impl);
   }
 
   void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
-    if (layer_tree_host()->settings().impl_side_painting)
+    if (impl->settings().impl_side_painting)
       PerformTest(impl);
   }
 
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index cc536f4..48e6530 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -103,7 +103,7 @@
       // Only valid for single-threaded impl-side painting, which activates
       // immediately and will try to draw again when content has finished.
       DCHECK(!host_impl->proxy()->HasImplThread());
-      DCHECK(layer_tree_host()->settings().impl_side_painting);
+      DCHECK(host_impl->settings().impl_side_painting);
       return draw_result;
     }
     EXPECT_EQ(DRAW_SUCCESS, draw_result);
@@ -876,7 +876,7 @@
     FakeContentLayerImpl* child_content = NULL;
     FakeContentLayerImpl* grandchild_content = NULL;
 
-    if (layer_tree_host()->settings().impl_side_painting) {
+    if (host_impl->settings().impl_side_painting) {
       root_picture = static_cast<FakePictureLayerImpl*>(
           host_impl->active_tree()->root_layer());
       child_picture =
@@ -896,7 +896,7 @@
     ++num_commits_;
     switch (num_commits_) {
       case 1:
-        if (layer_tree_host()->settings().impl_side_painting) {
+        if (host_impl->settings().impl_side_painting) {
           EXPECT_EQ(0u, root_picture->release_resources_count());
           EXPECT_EQ(0u, child_picture->release_resources_count());
           EXPECT_EQ(0u, grandchild_picture->release_resources_count());
@@ -911,7 +911,7 @@
         times_to_fail_create_ = 1;
         break;
       case 2:
-        if (layer_tree_host()->settings().impl_side_painting) {
+        if (host_impl->settings().impl_side_painting) {
           EXPECT_TRUE(root_picture->release_resources_count());
           EXPECT_TRUE(child_picture->release_resources_count());
           EXPECT_TRUE(grandchild_picture->release_resources_count());
@@ -1269,7 +1269,7 @@
   virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) = 0;
 
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
-    if (!layer_tree_host()->settings().impl_side_painting) {
+    if (!impl->settings().impl_side_painting) {
       StepCompleteOnImplThread(impl);
       PostStepCompleteToMainThread();
       ++time_step_;
@@ -1277,7 +1277,7 @@
   }
 
   void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
-    if (layer_tree_host()->settings().impl_side_painting) {
+    if (impl->settings().impl_side_painting) {
       StepCompleteOnImplThread(impl);
       PostStepCompleteToMainThread();
       ++time_step_;
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 622143d..3abc4dc 100644
--- a/cc/trees/layer_tree_host_unittest_no_message_loop.cc
+++ b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
@@ -106,7 +106,7 @@
     settings.verify_property_trees = true;
     settings.raster_enabled = false;
     layer_tree_host_ = LayerTreeHost::CreateSingleThreaded(
-        this, this, nullptr, nullptr, settings, nullptr, nullptr);
+        this, this, nullptr, nullptr, nullptr, settings, nullptr, nullptr);
     layer_tree_host_->SetViewportSize(size_);
     layer_tree_host_->SetRootLayer(root_layer_);
   }
diff --git a/cc/trees/layer_tree_host_unittest_picture.cc b/cc/trees/layer_tree_host_unittest_picture.cc
index 5ce47cd..7d3d525 100644
--- a/cc/trees/layer_tree_host_unittest_picture.cc
+++ b/cc/trees/layer_tree_host_unittest_picture.cc
@@ -565,7 +565,8 @@
 
 // Multi-thread only because in single thread you can't pinch zoom on the
 // compositor thread.
-MULTI_THREAD_IMPL_TEST_F(LayerTreeHostPictureTestRSLLMembershipWithScale);
+// Disabled due to flakiness. See http://crbug.com/460581
+// MULTI_THREAD_IMPL_TEST_F(LayerTreeHostPictureTestRSLLMembershipWithScale);
 
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 547ece7..be972e9 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -429,14 +429,19 @@
         PostSetNeedsCommitToMainThread();
         break;
       case 1:
-        EXPECT_VECTOR_EQ(scroll_layer->BaseScrollOffset(), scroll_amount_);
-        EXPECT_VECTOR_EQ(scroll_layer->ScrollDelta(), gfx::Vector2dF());
+        EXPECT_VECTOR_EQ(scroll_layer->BaseScrollOffset(),
+                         gfx::ToFlooredVector2d(scroll_amount_));
+        EXPECT_VECTOR_EQ(scroll_layer->ScrollDelta(),
+                         gfx::Vector2dF(fmod(scroll_amount_.x(), 1.0f), 0.0f));
         PostSetNeedsCommitToMainThread();
         break;
       case 2:
-        EXPECT_VECTOR_EQ(scroll_layer->BaseScrollOffset(),
-                         (scroll_amount_ + scroll_amount_));
-        EXPECT_VECTOR_EQ(scroll_layer->ScrollDelta(), gfx::Vector2dF());
+        EXPECT_VECTOR_EQ(
+            scroll_layer->BaseScrollOffset(),
+            gfx::ToFlooredVector2d(scroll_amount_ + scroll_amount_));
+        EXPECT_VECTOR_EQ(
+            scroll_layer->ScrollDelta(),
+            gfx::Vector2dF(fmod(2.0f * scroll_amount_.x(), 1.0f), 0.0f));
         EndTest();
         break;
     }
@@ -1121,14 +1126,10 @@
   ASSERT_TRUE(impl_thread.message_loop_proxy().get());
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
-  scoped_ptr<LayerTreeHost> layer_tree_host =
-      LayerTreeHost::CreateThreaded(&client,
-                                    shared_bitmap_manager.get(),
-                                    NULL,
-                                    settings,
-                                    base::MessageLoopProxy::current(),
-                                    impl_thread.message_loop_proxy(),
-                                    nullptr);
+  scoped_ptr<LayerTreeHost> layer_tree_host = LayerTreeHost::CreateThreaded(
+      &client, shared_bitmap_manager.get(), NULL, NULL, settings,
+      base::MessageLoopProxy::current(), impl_thread.message_loop_proxy(),
+      nullptr);
 
   impl_thread.message_loop_proxy()
       ->PostTask(FROM_HERE,
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index a27630e..b5f8aed 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -982,7 +982,8 @@
   return layer_tree_host_impl_->animation_registrar();
 }
 
-void LayerTreeImpl::GetAllTilesForTracing(std::set<const Tile*>* tiles) const {
+void LayerTreeImpl::GetAllTilesAndPrioritiesForTracing(
+    std::map<const Tile*, TilePriority>* tile_map) const {
   typedef LayerIterator<LayerImpl> LayerIteratorType;
   LayerIteratorType end = LayerIteratorType::End(&render_surface_layer_list_);
   for (LayerIteratorType it =
@@ -992,7 +993,7 @@
     if (!it.represents_itself())
       continue;
     LayerImpl* layer_impl = *it;
-    layer_impl->GetAllTilesForTracing(tiles);
+    layer_impl->GetAllTilesAndPrioritiesForTracing(tile_map);
   }
 }
 
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 15e0f0c..a8c58d1 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -116,7 +116,8 @@
 
   // Tracing methods.
   // ---------------------------------------------------------------------------
-  void GetAllTilesForTracing(std::set<const Tile*>* tiles) const;
+  void GetAllTilesAndPrioritiesForTracing(
+      std::map<const Tile*, TilePriority>* tile_map) const;
   void AsValueInto(base::trace_event::TracedValue* dict) const;
 
   // Other public methods
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 06df3c2..c4abd52 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/layer_tree_host_common_test.h"
 #include "cc/test/test_shared_bitmap_manager.h"
+#include "cc/test/test_task_graph_runner.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "ui/gfx/geometry/size_conversions.h"
 
@@ -25,8 +26,8 @@
     LayerTreeSettings settings;
     settings.layer_transforms_should_scale_layer_contents = true;
     settings.scrollbar_show_scale_threshold = 1.1f;
-    host_impl_.reset(
-        new FakeLayerTreeHostImpl(settings, &proxy_, &shared_bitmap_manager_));
+    host_impl_.reset(new FakeLayerTreeHostImpl(
+        settings, &proxy_, &shared_bitmap_manager_, &task_graph_runner_));
     EXPECT_TRUE(host_impl_->InitializeRenderer(FakeOutputSurface::Create3d()));
   }
 
@@ -40,6 +41,7 @@
 
  private:
   TestSharedBitmapManager shared_bitmap_manager_;
+  TestTaskGraphRunner task_graph_runner_;
   FakeImplProxy proxy_;
   scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
 };
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index 96c9d16..09314ad 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -70,9 +70,30 @@
       use_occlusion_for_tile_prioritization(false),
       record_full_layer(false),
       use_display_lists(false),
-      verify_property_trees(false) {
+      verify_property_trees(false),
+      gather_pixel_refs(false) {
 }
 
 LayerTreeSettings::~LayerTreeSettings() {}
 
+SchedulerSettings LayerTreeSettings::ToSchedulerSettings() const {
+  SchedulerSettings scheduler_settings;
+  scheduler_settings.use_external_begin_frame_source =
+      use_external_begin_frame_source;
+  scheduler_settings.main_frame_before_activation_enabled =
+      main_frame_before_activation_enabled;
+  scheduler_settings.impl_side_painting = impl_side_painting;
+  scheduler_settings.timeout_and_draw_when_animation_checkerboards =
+      timeout_and_draw_when_animation_checkerboards;
+  scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ =
+      maximum_number_of_failed_draws_before_draw_is_forced_;
+  scheduler_settings.using_synchronous_renderer_compositor =
+      using_synchronous_renderer_compositor;
+  scheduler_settings.throttle_frame_production = throttle_frame_production;
+  scheduler_settings.main_thread_should_always_be_low_latency = false;
+  scheduler_settings.background_frame_interval =
+      base::TimeDelta::FromSecondsD(1.0 / background_animation_rate);
+  return scheduler_settings;
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index 4554362..1f61f89 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -9,6 +9,7 @@
 #include "cc/base/cc_export.h"
 #include "cc/debug/layer_tree_debug_state.h"
 #include "cc/output/renderer_settings.h"
+#include "cc/scheduler/scheduler_settings.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -82,8 +83,11 @@
   bool record_full_layer;
   bool use_display_lists;
   bool verify_property_trees;
+  bool gather_pixel_refs;
 
   LayerTreeDebugState initial_debug_state;
+
+  SchedulerSettings ToSchedulerSettings() const;
 };
 
 }  // namespace cc
diff --git a/cc/trees/occlusion_tracker_perftest.cc b/cc/trees/occlusion_tracker_perftest.cc
index e53a05f..854dacd 100644
--- a/cc/trees/occlusion_tracker_perftest.cc
+++ b/cc/trees/occlusion_tracker_perftest.cc
@@ -36,13 +36,9 @@
   void CreateHost() {
     LayerTreeSettings settings;
     shared_bitmap_manager_.reset(new TestSharedBitmapManager());
-    host_impl_ = LayerTreeHostImpl::Create(settings,
-                                           &client_,
-                                           &proxy_,
-                                           &stats_,
-                                           shared_bitmap_manager_.get(),
-                                           NULL,
-                                           1);
+    host_impl_ =
+        LayerTreeHostImpl::Create(settings, &client_, &proxy_, &stats_,
+                                  shared_bitmap_manager_.get(), NULL, NULL, 1);
     host_impl_->InitializeRenderer(FakeOutputSurface::Create3d());
 
     scoped_ptr<LayerImpl> root_layer = LayerImpl::Create(active_tree(), 1);
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 0db2ad7..3da729e 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -27,8 +27,6 @@
   Layer* render_target;
   int clip_tree_parent;
   int opacity_tree_parent;
-  gfx::Vector2dF offset_to_transform_tree_parent;
-  gfx::Vector2dF offset_to_transform_fixed_parent;
   const Layer* page_scale_layer;
   float page_scale_factor;
   float device_scale_factor;
@@ -131,11 +129,17 @@
 
   Layer* transform_parent = GetTransformParent(data_from_ancestor, layer);
 
-  // May be non-zero if layer is fixed or has a scroll parent.
   gfx::Vector2dF parent_offset;
   if (transform_parent) {
-    // TODO(vollick): This is to mimic existing bugs (crbug.com/441447).
-    if (!is_fixed) {
+    if (layer->scroll_parent()) {
+      gfx::Transform to_parent;
+      Layer* source = layer->parent();
+      parent_offset += source->offset_to_transform_parent();
+      data_from_ancestor.transform_tree->ComputeTransform(
+          source->transform_tree_index(),
+          transform_parent->transform_tree_index(), &to_parent);
+      parent_offset += to_parent.To2dTranslation();
+    } else if (!is_fixed) {
       parent_offset = transform_parent->offset_to_transform_parent();
     } else if (data_from_ancestor.transform_tree_parent !=
                data_from_ancestor.transform_fixed_parent) {
@@ -150,18 +154,6 @@
       fixed_offset += parent_to_parent.To2dTranslation();
       parent_offset += fixed_offset;
     }
-
-    gfx::Transform to_parent;
-    Layer* source = data_from_ancestor.transform_tree_parent;
-    if (layer->scroll_parent()) {
-      source = layer->parent();
-      parent_offset += layer->parent()->offset_to_transform_parent();
-    }
-    data_from_ancestor.transform_tree->ComputeTransform(
-        source->transform_tree_index(),
-        transform_parent->transform_tree_index(), &to_parent);
-
-    parent_offset += to_parent.To2dTranslation();
   }
 
   if (layer->IsContainerForFixedPositionLayers() || is_root)
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 1ecce4d..b6084bc 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -5,6 +5,7 @@
 #include "cc/trees/single_thread_proxy.h"
 
 #include "base/auto_reset.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/debug/benchmark_instrumentation.h"
 #include "cc/debug/devtools_instrumentation.h"
@@ -96,7 +97,8 @@
   DebugScopedSetImplThread impl(this);
   if (layer_tree_host_->settings().single_thread_proxy_scheduler &&
       !scheduler_on_impl_thread_) {
-    SchedulerSettings scheduler_settings(layer_tree_host_->settings());
+    SchedulerSettings scheduler_settings(
+        layer_tree_host_->settings().ToSchedulerSettings());
     // SingleThreadProxy should run in main thread low latency mode.
     scheduler_settings.main_thread_should_always_be_low_latency = true;
     scheduler_on_impl_thread_ =
@@ -208,6 +210,10 @@
   TRACE_EVENT0("cc", "SingleThreadProxy::DoCommit");
   DCHECK(Proxy::IsMainThread());
 
+  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("461509 SingleThreadProxy::DoCommit1"));
   commit_requested_ = false;
   layer_tree_host_->WillCommit();
   devtools_instrumentation::ScopedCommitTrace commit_task(
@@ -215,6 +221,11 @@
 
   // Commit immediately.
   {
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile2(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoCommit2"));
     DebugScopedSetMainThreadBlocked main_thread_blocked(this);
     DebugScopedSetImplThread impl(this);
 
@@ -228,21 +239,47 @@
 
     if (PrioritizedResourceManager* contents_texture_manager =
         layer_tree_host_->contents_texture_manager()) {
+      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+      // is fixed.
+      tracked_objects::ScopedTracker tracking_profile3(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "461509 SingleThreadProxy::DoCommit3"));
       contents_texture_manager->PushTexturePrioritiesToBackings();
     }
     layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get());
 
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile4(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoCommit4"));
     scoped_ptr<ResourceUpdateController> update_controller =
         ResourceUpdateController::Create(
             NULL,
             MainThreadTaskRunner(),
             queue_for_commit_.Pass(),
             layer_tree_host_impl_->resource_provider());
+
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile5(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoCommit5"));
     update_controller->Finalize();
 
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile6(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoCommit6"));
     if (layer_tree_host_impl_->EvictedUIResourcesExist())
       layer_tree_host_->RecreateUIResources();
 
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile7(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoCommit7"));
     layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get());
 
 #if DCHECK_IS_ON()
@@ -255,6 +292,11 @@
 #endif
 
     if (layer_tree_host_->settings().impl_side_painting) {
+      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+      // is fixed.
+      tracked_objects::ScopedTracker tracking_profile8(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "461509 SingleThreadProxy::DoCommit8"));
       // Commit goes directly to the active tree, but we need to synchronously
       // "activate" the tree still during commit to satisfy any potential
       // SetNextCommitWaitsForActivation calls.  Unfortunately, the tree
@@ -262,6 +304,11 @@
       // the flag to force the tree to not draw until textures are ready.
       NotifyReadyToActivate();
     } else {
+      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+      // is fixed.
+      tracked_objects::ScopedTracker tracking_profile9(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "461509 SingleThreadProxy::DoCommit9"));
       CommitComplete();
     }
   }
@@ -596,6 +643,12 @@
     DebugScopedSetImplThread impl(this);
     base::AutoReset<bool> mark_inside(&inside_draw_, true);
 
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile1(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite1"));
+
     // We guard PrepareToDraw() with CanDraw() because it always returns a valid
     // frame, so can only be used when such a frame is possible. Since
     // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
@@ -606,16 +659,47 @@
 
     timing_history_.DidStartDrawing();
 
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile2(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite2"));
     draw_result = layer_tree_host_impl_->PrepareToDraw(frame);
     draw_frame = draw_result == DRAW_SUCCESS;
-    if (draw_frame)
+    if (draw_frame) {
+      // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+      // is fixed.
+      tracked_objects::ScopedTracker tracking_profile3(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "461509 SingleThreadProxy::DoComposite3"));
       layer_tree_host_impl_->DrawLayers(frame, frame_begin_time);
+    }
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile4(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite4"));
     layer_tree_host_impl_->DidDrawAllLayers(*frame);
 
     bool start_ready_animations = draw_frame;
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile5(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite5"));
     layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile6(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite6"));
     layer_tree_host_impl_->ResetCurrentBeginFrameArgsForNextFrame();
 
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile7(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite7"));
     timing_history_.DidFinishDrawing();
   }
 
@@ -634,8 +718,18 @@
 
     BlockingTaskRunner::CapturePostTasks blocked(
         blocking_main_thread_task_runner());
+    // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509
+    // is fixed.
+    tracked_objects::ScopedTracker tracking_profile8(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "461509 SingleThreadProxy::DoComposite8"));
     layer_tree_host_impl_->SwapBuffers(*frame);
   }
+  // TODO(robliao): Remove ScopedTracker below once https://crbug.com/461509 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile9(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "461509 SingleThreadProxy::DoComposite9"));
   DidCommitAndDrawFrame();
 
   return draw_result;
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 6e1a0d9..ff2ca46 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -1165,7 +1165,8 @@
   DCHECK(IsImplThread());
   impl().layer_tree_host_impl =
       layer_tree_host()->CreateLayerTreeHostImpl(this);
-  SchedulerSettings scheduler_settings(layer_tree_host()->settings());
+  SchedulerSettings scheduler_settings(
+      layer_tree_host()->settings().ToSchedulerSettings());
   impl().scheduler = Scheduler::Create(
                          this,
                          scheduler_settings,
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index d48f821..a17bec8 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -556,13 +556,8 @@
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHostImpl> host_impl =
-      LayerTreeHostImpl::Create(settings,
-                                NULL,
-                                &proxy,
-                                &stats_instrumentation,
-                                shared_bitmap_manager.get(),
-                                NULL,
-                                0);
+      LayerTreeHostImpl::Create(settings, NULL, &proxy, &stats_instrumentation,
+                                shared_bitmap_manager.get(), NULL, NULL, 0);
 
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
   host_->SetRootLayer(layer_tree_root);
@@ -595,13 +590,8 @@
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHostImpl> host_impl =
-      LayerTreeHostImpl::Create(settings,
-                                NULL,
-                                &proxy,
-                                &stats_instrumentation,
-                                shared_bitmap_manager.get(),
-                                NULL,
-                                0);
+      LayerTreeHostImpl::Create(settings, NULL, &proxy, &stats_instrumentation,
+                                shared_bitmap_manager.get(), NULL, NULL, 0);
 
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
   scoped_refptr<Layer> scroll_parent = Layer::Create();
@@ -668,13 +658,8 @@
   scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
       new TestSharedBitmapManager());
   scoped_ptr<LayerTreeHostImpl> host_impl =
-      LayerTreeHostImpl::Create(settings,
-                                NULL,
-                                &proxy,
-                                &stats_instrumentation,
-                                shared_bitmap_manager.get(),
-                                NULL,
-                                0);
+      LayerTreeHostImpl::Create(settings, NULL, &proxy, &stats_instrumentation,
+                                shared_bitmap_manager.get(), NULL, NULL, 0);
 
   scoped_refptr<Layer> layer_tree_root = Layer::Create();
   scoped_refptr<Layer> clip_parent = Layer::Create();
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index d5b799e..af23813 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -82,6 +82,7 @@
   {'name': 'scissor_test'},
   {'name': 'stencil_test',
    'state_flag': 'framebuffer_state_.clear_state_dirty'},
+  {'name': 'rasterizer_discard', 'es3': True},
 ]
 
 _STATES = {
@@ -898,7 +899,10 @@
   },
   'Capability': {
     'type': 'GLenum',
-    'valid': ["GL_%s" % cap['name'].upper() for cap in _CAPABILITY_FLAGS],
+    'valid': ["GL_%s" % cap['name'].upper() for cap in _CAPABILITY_FLAGS
+        if 'es3' not in cap or cap['es3'] != True],
+    'valid_es3': ["GL_%s" % cap['name'].upper() for cap in _CAPABILITY_FLAGS
+        if 'es3' in cap and cap['es3'] == True],
     'invalid': [
       'GL_CLIP_PLANE0',
       'GL_POINT_SPRITE',
@@ -2676,7 +2680,7 @@
   },
   'MapTexSubImage2DCHROMIUM': {
     'gen_cmd': False,
-    'extension': True,
+    'extension': "CHROMIUM_sub_image",
     'chromium': True,
     'client_test': False,
     'pepper_interface': 'ChromiumMapSub',
@@ -3082,7 +3086,7 @@
   },
   'UnmapTexSubImage2DCHROMIUM': {
     'gen_cmd': False,
-    'extension': True,
+    'extension': "CHROMIUM_sub_image",
     'chromium': True,
     'client_test': False,
     'pepper_interface': 'ChromiumMapSub',
@@ -3265,6 +3269,7 @@
     'unit_test': False,
     'pepper_interface': 'Query',
     'not_shared': 'True',
+    'extension': "occlusion_query_EXT",
   },
   'DeleteQueriesEXT': {
     'type': 'DELn',
@@ -3273,11 +3278,13 @@
     'resource_types': 'Queries',
     'unit_test': False,
     'pepper_interface': 'Query',
+    'extension': "occlusion_query_EXT",
   },
   'IsQueryEXT': {
     'gen_cmd': False,
     'client_test': False,
     'pepper_interface': 'Query',
+    'extension': "occlusion_query_EXT",
   },
   'BeginQueryEXT': {
     'type': 'Manual',
@@ -3285,6 +3292,7 @@
     'data_transfer_methods': ['shm'],
     'gl_test_func': 'glBeginQuery',
     'pepper_interface': 'Query',
+    'extension': "occlusion_query_EXT",
   },
   'BeginTransformFeedback': {
     'unsafe': True,
@@ -3295,6 +3303,7 @@
     'gl_test_func': 'glEndnQuery',
     'client_test': False,
     'pepper_interface': 'Query',
+    'extension': "occlusion_query_EXT",
   },
   'EndTransformFeedback': {
     'unsafe': True,
@@ -3304,12 +3313,14 @@
     'client_test': False,
     'gl_test_func': 'glGetQueryiv',
     'pepper_interface': 'Query',
+    'extension': "occlusion_query_EXT",
   },
   'GetQueryObjectuivEXT': {
     'gen_cmd': False,
     'client_test': False,
     'gl_test_func': 'glGetQueryObjectuiv',
     'pepper_interface': 'Query',
+    'extension': "occlusion_query_EXT",
   },
   'BindUniformLocationCHROMIUM': {
     'type': 'GLchar',
@@ -3396,7 +3407,7 @@
   'ShallowFlushCHROMIUM': {
     'impl_func': False,
     'gen_cmd': False,
-    'extension': True,
+    'extension': "CHROMIUM_miscellaneous",
     'chromium': True,
     'client_test': False,
   },
@@ -4198,6 +4209,46 @@
                (func.return_type, func.original_name,
                 func.MakeTypedOriginalArgString("")))
 
+  def WriteMojoGLES2ImplHeader(self, func, file):
+    """Writes the Mojo GLES2 implementation header."""
+    file.Write("%s %s(%s) override;\n" %
+               (func.return_type, func.original_name,
+                func.MakeTypedOriginalArgString("")))
+
+  def WriteMojoGLES2Impl(self, func, file):
+    """Writes the Mojo GLES2 implementation."""
+    file.Write("%s MojoGLES2Impl::%s(%s) {\n" %
+               (func.return_type, func.original_name,
+                func.MakeTypedOriginalArgString("")))
+    # TODO(alhaad): Add Mojo C thunk for each of the following methods and
+    # remove this.
+    func_list = ["GenQueriesEXT", "BeginQueryEXT", "MapTexSubImage2DCHROMIUM",
+                 "UnmapTexSubImage2DCHROMIUM", "DeleteQueriesEXT",
+                 "EndQueryEXT", "GetQueryObjectuivEXT", "ShallowFlushCHROMIUM"]
+    if func.original_name in func_list:
+      file.Write("return static_cast<gpu::gles2::GLES2Interface*>"
+                 "(MojoGLES2GetGLES2Interface(context_))->" +
+                 func.original_name + "(" + func.MakeOriginalArgString("") +
+                 ");")
+      file.Write("}")
+      return
+
+    extensions = ["CHROMIUM_sync_point", "CHROMIUM_texture_mailbox"]
+    if func.IsCoreGLFunction() or func.GetInfo("extension") in extensions:
+      file.Write("MojoGLES2MakeCurrent(context_);");
+      func_return = "gl" + func.original_name + "(" + \
+          func.MakeOriginalArgString("") + ");"
+      if func.return_type == "void":
+        file.Write(func_return);
+      else:
+        file.Write("return " + func_return);
+    else:
+      file.Write("NOTREACHED() << \"Unimplemented %s.\";\n" %
+                 func.original_name);
+      if func.return_type != "void":
+        file.Write("return 0;")
+    file.Write("}")
+
   def WriteGLES2InterfaceStub(self, func, file):
     """Writes the GLES2 Interface stub declaration."""
     file.Write("%s %s(%s) override;\n" %
@@ -8907,6 +8958,14 @@
     """Writes the GLES2 Interface declaration."""
     self.type_handler.WriteGLES2InterfaceHeader(self, file)
 
+  def WriteMojoGLES2ImplHeader(self, file):
+    """Writes the Mojo GLES2 implementation header declaration."""
+    self.type_handler.WriteMojoGLES2ImplHeader(self, file)
+
+  def WriteMojoGLES2Impl(self, file):
+    """Writes the Mojo GLES2 implementation declaration."""
+    self.type_handler.WriteMojoGLES2Impl(self, file)
+
   def WriteGLES2InterfaceStub(self, file):
     """Writes the GLES2 Interface Stub declaration."""
     self.type_handler.WriteGLES2InterfaceStub(self, file)
@@ -9534,20 +9593,31 @@
     file.Write("""
 void ContextState::InitCapabilities(const ContextState* prev_state) const {
 """)
-    def WriteCapabilities(test_prev):
+    def WriteCapabilities(test_prev, es3_caps):
       for capability in _CAPABILITY_FLAGS:
         capability_name = capability['name']
+        capability_es3 = 'es3' in capability and capability['es3'] == True
+        if capability_es3 and not es3_caps or not capability_es3 and es3_caps:
+          continue
         if test_prev:
           file.Write("""  if (prev_state->enable_flags.cached_%s !=
-                              enable_flags.cached_%s)\n""" %
+                              enable_flags.cached_%s) {\n""" %
                      (capability_name, capability_name))
         file.Write("    EnableDisable(GL_%s, enable_flags.cached_%s);\n" %
                    (capability_name.upper(), capability_name))
+        if test_prev:
+          file.Write("  }")
 
     file.Write("  if (prev_state) {")
-    WriteCapabilities(True)
+    WriteCapabilities(True, False)
+    file.Write("    if (feature_info_->IsES3Capable()) {\n")
+    WriteCapabilities(True, True)
+    file.Write("    }\n")
     file.Write("  } else {")
-    WriteCapabilities(False)
+    WriteCapabilities(False, False)
+    file.Write("    if (feature_info_->IsES3Capable()) {\n")
+    WriteCapabilities(False, True)
+    file.Write("    }\n")
     file.Write("  }")
 
     file.Write("""}
@@ -9783,13 +9853,24 @@
         filename % 0,
         "// It is included by gles2_cmd_decoder_unittest_base.cc\n")
     file.Write(
-"""void GLES2DecoderTestBase::SetupInitCapabilitiesExpectations() {
-""")
+"""void GLES2DecoderTestBase::SetupInitCapabilitiesExpectations(
+      bool es3_capable) {""")
     for capability in _CAPABILITY_FLAGS:
-      file.Write("  ExpectEnableDisable(GL_%s, %s);\n" %
-                 (capability['name'].upper(),
-                  ('false', 'true')['default' in capability]))
-    file.Write("""}
+      capability_es3 = 'es3' in capability and capability['es3'] == True
+      if not capability_es3:
+        file.Write("  ExpectEnableDisable(GL_%s, %s);\n" %
+                   (capability['name'].upper(),
+                    ('false', 'true')['default' in capability]))
+
+    file.Write("  if (es3_capable) {")
+    for capability in _CAPABILITY_FLAGS:
+      capability_es3 = 'es3' in capability and capability['es3'] == True
+      if capability_es3:
+        file.Write("    ExpectEnableDisable(GL_%s, %s);\n" %
+                   (capability['name'].upper(),
+                    ('false', 'true')['default' in capability]))
+    file.Write("""  }
+}
 
 void GLES2DecoderTestBase::SetupInitStateExpectations() {
 """)
@@ -9930,6 +10011,68 @@
     file.Close()
     self.generated_cpp_filenames.append(file.filename)
 
+  def WriteMojoGLES2ImplHeader(self, filename):
+    """Writes the Mojo GLES2 implementation header."""
+    file = CHeaderWriter(
+        filename,
+        "// This file is included by gles2_interface.h to declare the\n"
+        "// GL api functions.\n")
+
+    code = """
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h"
+
+namespace mojo {
+
+class MojoGLES2Impl : public gpu::gles2::GLES2Interface {
+ public:
+  explicit MojoGLES2Impl(MojoGLES2Context context) {
+    context_ = context;
+  }
+  ~MojoGLES2Impl() override {}
+    """
+    file.Write(code);
+    for func in self.original_functions:
+      func.WriteMojoGLES2ImplHeader(file)
+    code = """
+ private:
+  MojoGLES2Context context_;
+};
+
+}  // namespace mojo
+    """
+    file.Write(code);
+    file.Close()
+    self.generated_cpp_filenames.append(file.filename)
+
+  def WriteMojoGLES2Impl(self, filename):
+    """Writes the Mojo GLES2 implementation."""
+    file = CWriter(filename)
+    file.Write(_LICENSE)
+    file.Write(_DO_NOT_EDIT_WARNING)
+
+    code = """
+#include "mojo/gpu/mojo_gles2_impl_autogen.h"
+
+#include "base/logging.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/chromium_sync_point.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/chromium_texture_mailbox.h"
+#include "third_party/mojo/src/mojo/public/c/gles2/gles2.h"
+
+namespace mojo {
+
+    """
+    file.Write(code);
+    for func in self.original_functions:
+      func.WriteMojoGLES2Impl(file)
+    code = """
+
+}  // namespace mojo
+    """
+    file.Write(code);
+    file.Close()
+    self.generated_cpp_filenames.append(file.filename)
+
   def WriteGLES2InterfaceStub(self, filename):
     """Writes the GLES2 interface stub header."""
     file = CHeaderWriter(
@@ -10448,6 +10591,10 @@
     "gpu/command_buffer/common/gles2_cmd_format_test_autogen.h")
   gen.WriteGLES2InterfaceHeader(
     "gpu/command_buffer/client/gles2_interface_autogen.h")
+  gen.WriteMojoGLES2ImplHeader(
+    "mojo/gpu/mojo_gles2_impl_autogen.h")
+  gen.WriteMojoGLES2Impl(
+    "mojo/gpu/mojo_gles2_impl_autogen.cc")
   gen.WriteGLES2InterfaceStub(
     "gpu/command_buffer/client/gles2_interface_stub_autogen.h")
   gen.WriteGLES2InterfaceStubImpl(
@@ -10500,6 +10647,15 @@
   gen.WriteMojoGLCallVisitorForExtension(
       mojo_gles2_prefix + "_chromium_sync_point_autogen.h",
       "CHROMIUM_sync_point")
+  gen.WriteMojoGLCallVisitorForExtension(
+      mojo_gles2_prefix + "_chromium_sub_image_autogen.h",
+      "CHROMIUM_sub_image")
+  gen.WriteMojoGLCallVisitorForExtension(
+      mojo_gles2_prefix + "_chromium_miscellaneous_autogen.h",
+      "CHROMIUM_miscellaneous")
+  gen.WriteMojoGLCallVisitorForExtension(
+      mojo_gles2_prefix + "_occlusion_query_ext_autogen.h",
+      "occlusion_query_EXT")
 
   Format(gen.generated_cpp_filenames)
 
diff --git a/gpu/command_buffer/client/client_context_state.h b/gpu/command_buffer/client/client_context_state.h
index f5a93a6..a92c04a 100644
--- a/gpu/command_buffer/client/client_context_state.h
+++ b/gpu/command_buffer/client/client_context_state.h
@@ -7,7 +7,7 @@
 #ifndef GPU_COMMAND_BUFFER_CLIENT_CLIENT_CONTEXT_STATE_H_
 #define GPU_COMMAND_BUFFER_CLIENT_CLIENT_CONTEXT_STATE_H_
 
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
 #include <vector>
 #include "gles2_impl_export.h"
 
diff --git a/gpu/command_buffer/client/client_context_state_autogen.h b/gpu/command_buffer/client/client_context_state_autogen.h
index 72a4f72..aa4b8fd 100644
--- a/gpu/command_buffer/client/client_context_state_autogen.h
+++ b/gpu/command_buffer/client/client_context_state_autogen.h
@@ -23,6 +23,7 @@
   bool sample_coverage;
   bool scissor_test;
   bool stencil_test;
+  bool rasterizer_discard;
 };
 
 #endif  // GPU_COMMAND_BUFFER_CLIENT_CLIENT_CONTEXT_STATE_AUTOGEN_H_
diff --git a/gpu/command_buffer/client/client_context_state_impl_autogen.h b/gpu/command_buffer/client/client_context_state_impl_autogen.h
index cff14f7..d0ce722 100644
--- a/gpu/command_buffer/client/client_context_state_impl_autogen.h
+++ b/gpu/command_buffer/client/client_context_state_impl_autogen.h
@@ -21,7 +21,8 @@
       sample_alpha_to_coverage(false),
       sample_coverage(false),
       scissor_test(false),
-      stencil_test(false) {
+      stencil_test(false),
+      rasterizer_discard(false) {
 }
 
 bool ClientContextState::SetCapabilityState(GLenum cap,
@@ -83,6 +84,12 @@
         enable_flags.stencil_test = enabled;
       }
       return true;
+    case GL_RASTERIZER_DISCARD:
+      if (enable_flags.rasterizer_discard != enabled) {
+        *changed = true;
+        enable_flags.rasterizer_discard = enabled;
+      }
+      return true;
     default:
       return false;
   }
@@ -116,6 +123,9 @@
     case GL_STENCIL_TEST:
       *enabled = enable_flags.stencil_test;
       return true;
+    case GL_RASTERIZER_DISCARD:
+      *enabled = enable_flags.rasterizer_discard;
+      return true;
     default:
       return false;
   }
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index fe59c2a..139dc65 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -4975,7 +4975,7 @@
     SetGLError(GL_INVALID_VALUE, func, "size < 0");
     return false;
   }
-  if (!FitInt32NonNegative<GLsizeiptr>(size)) {
+  if (!base::IsValueInRangeForNumericType<int32_t>(size)) {
     SetGLError(GL_INVALID_OPERATION, func, "size more than 32-bit");
     return false;
   }
@@ -4987,7 +4987,7 @@
     SetGLError(GL_INVALID_VALUE, func, "offset < 0");
     return false;
   }
-  if (!FitInt32NonNegative<GLintptr>(offset)) {
+  if (!base::IsValueInRangeForNumericType<int32_t>(offset)) {
     SetGLError(GL_INVALID_OPERATION, func, "offset more than 32-bit");
     return false;
   }
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index 22f826f..caf30af 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -404,13 +404,14 @@
 // Return the number of bytes per element, based on the element type.
 int BytesPerElement(int type) {
   switch (type) {
+    case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+      return 8;
     case GL_FLOAT:
     case GL_UNSIGNED_INT_24_8_OES:
     case GL_UNSIGNED_INT:
     case GL_UNSIGNED_INT_2_10_10_10_REV:
     case GL_UNSIGNED_INT_10F_11F_11F_REV:
     case GL_UNSIGNED_INT_5_9_9_9_REV:
-    case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
       return 4;
     case GL_HALF_FLOAT_OES:
     case GL_UNSIGNED_SHORT:
@@ -430,12 +431,18 @@
 }  // anonymous namespace
 
 uint32 GLES2Util::ComputeImageGroupSize(int format, int type) {
-  return BytesPerElement(type) * ElementsPerGroup(format, type);
+  int bytes_per_element = BytesPerElement(type);
+  DCHECK_GE(8, bytes_per_element);
+  int elements_per_group = ElementsPerGroup(format, type);
+  DCHECK_GE(4, elements_per_group);
+  return  bytes_per_element * elements_per_group;
 }
 
 bool GLES2Util::ComputeImagePaddedRowSize(
         int width, int format, int type, int unpack_alignment,
         uint32* padded_row_size) {
+  DCHECK(unpack_alignment == 1 || unpack_alignment == 2 ||
+         unpack_alignment == 4 || unpack_alignment == 8);
   uint32 bytes_per_group = ComputeImageGroupSize(format, type);
   uint32 unpadded_row_size;
   if (!SafeMultiplyUint32(width, bytes_per_group, &unpadded_row_size)) {
@@ -454,6 +461,8 @@
     int width, int height, int depth, int format, int type,
     int unpack_alignment, uint32* size, uint32* ret_unpadded_row_size,
     uint32* ret_padded_row_size) {
+  DCHECK(unpack_alignment == 1 || unpack_alignment == 2 ||
+         unpack_alignment == 4 || unpack_alignment == 8);
   uint32 bytes_per_group = ComputeImageGroupSize(format, type);
   uint32 row_size;
   if (!SafeMultiplyUint32(width, bytes_per_group, &row_size)) {
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h
index 1661247..e6b8bf0 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -14,6 +14,7 @@
 #include <string>
 #include <vector>
 
+#include "base/numerics/safe_math.h"
 #include "gpu/command_buffer/common/gles2_utils_export.h"
 
 namespace gpu {
@@ -25,44 +26,29 @@
 // Multiplies 2 32 bit unsigned numbers checking for overflow.
 // If there was no overflow returns true.
 inline bool SafeMultiplyUint32(uint32_t a, uint32_t b, uint32_t* dst) {
-  if (b == 0) {
-    *dst = 0;
-    return true;
-  }
-  uint32_t v = a * b;
-  if (v / b != a) {
-    *dst = 0;
-    return false;
-  }
-  *dst = v;
-  return true;
+  DCHECK(dst);
+  base::CheckedNumeric<uint32_t> checked = a;
+  checked *= b;
+  *dst = checked.ValueOrDefault(0);
+  return checked.IsValid();
 }
 
 // Does an add checking for overflow.  If there was no overflow returns true.
 inline bool SafeAddUint32(uint32_t a, uint32_t b, uint32_t* dst) {
-  if (a + b < a) {
-    *dst = 0;
-    return false;
-  }
-  *dst = a + b;
-  return true;
+  DCHECK(dst);
+  base::CheckedNumeric<uint32_t> checked = a;
+  checked += b;
+  *dst = checked.ValueOrDefault(0);
+  return checked.IsValid();
 }
 
 // Does an add checking for overflow.  If there was no overflow returns true.
 inline bool SafeAddInt32(int32_t a, int32_t b, int32_t* dst) {
-  int64_t sum64 = static_cast<int64_t>(a) + b;
-  int32_t sum32 = static_cast<int32_t>(sum64);
-  bool safe = sum64 == static_cast<int64_t>(sum32);
-  *dst = safe ? sum32 : 0;
-  return safe;
-}
-
-// Return false if |value| is more than a 32 bit integer can represent.
-template<typename T>
-inline bool FitInt32NonNegative(T value) {
-  const int32_t max = std::numeric_limits<int32_t>::max();
-  return (std::numeric_limits<T>::max() <= max ||
-          value <= static_cast<T>(max));
+  DCHECK(dst);
+  base::CheckedNumeric<int32_t> checked = a;
+  checked += b;
+  *dst = checked.ValueOrDefault(0);
+  return checked.IsValid();
 }
 
 // Utilties for GLES2 support.
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index fade8fa..42da99f 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -4560,6 +4560,7 @@
       {GL_SAMPLE_COVERAGE, "GL_SAMPLE_COVERAGE"},
       {GL_SCISSOR_TEST, "GL_SCISSOR_TEST"},
       {GL_STENCIL_TEST, "GL_STENCIL_TEST"},
+      {GL_RASTERIZER_DISCARD, "GL_RASTERIZER_DISCARD"},
   };
   return GLES2Util::GetQualifiedEnumString(string_table,
                                            arraysize(string_table), value);
@@ -4768,6 +4769,7 @@
       {GL_SAMPLE_COVERAGE, "GL_SAMPLE_COVERAGE"},
       {GL_SCISSOR_TEST, "GL_SCISSOR_TEST"},
       {GL_STENCIL_TEST, "GL_STENCIL_TEST"},
+      {GL_RASTERIZER_DISCARD, "GL_RASTERIZER_DISCARD"},
   };
   return GLES2Util::GetQualifiedEnumString(string_table,
                                            arraysize(string_table), value);
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc b/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc
index 58a6c38..8f7f94f 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils_unittest.cc
@@ -232,8 +232,8 @@
   EXPECT_TRUE(GLES2Util::ComputeImageDataSizes(
       kWidth, kHeight, 1, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV,
       1, &size, &unpadded_row_size, &padded_row_size));
-  EXPECT_EQ(kWidth * kHeight * 4, size);
-  EXPECT_EQ(kWidth * 4, padded_row_size);
+  EXPECT_EQ(kWidth * kHeight * 8, size);
+  EXPECT_EQ(kWidth * 8, padded_row_size);
   EXPECT_EQ(padded_row_size, unpadded_row_size);
 }
 
diff --git a/gpu/command_buffer/service/context_state_autogen.h b/gpu/command_buffer/service/context_state_autogen.h
index fcae244..405addd 100644
--- a/gpu/command_buffer/service/context_state_autogen.h
+++ b/gpu/command_buffer/service/context_state_autogen.h
@@ -32,6 +32,8 @@
   bool cached_scissor_test;
   bool stencil_test;
   bool cached_stencil_test;
+  bool rasterizer_discard;
+  bool cached_rasterizer_discard;
 };
 
 GLfloat blend_color_red;
@@ -150,6 +152,12 @@
         return;
       enable_flags.cached_stencil_test = enable;
       break;
+    case GL_RASTERIZER_DISCARD:
+      if (enable_flags.cached_rasterizer_discard == enable &&
+          !ignore_cached_state)
+        return;
+      enable_flags.cached_rasterizer_discard = enable;
+      break;
     default:
       NOTREACHED();
       return;
diff --git a/gpu/command_buffer/service/context_state_impl_autogen.h b/gpu/command_buffer/service/context_state_impl_autogen.h
index 037ac6c..467f1ce 100644
--- a/gpu/command_buffer/service/context_state_impl_autogen.h
+++ b/gpu/command_buffer/service/context_state_impl_autogen.h
@@ -30,7 +30,9 @@
       scissor_test(false),
       cached_scissor_test(false),
       stencil_test(false),
-      cached_stencil_test(false) {
+      cached_stencil_test(false),
+      rasterizer_discard(false),
+      cached_rasterizer_discard(false) {
 }
 
 void ContextState::Initialize() {
@@ -134,33 +136,49 @@
 
 void ContextState::InitCapabilities(const ContextState* prev_state) const {
   if (prev_state) {
-    if (prev_state->enable_flags.cached_blend != enable_flags.cached_blend)
+    if (prev_state->enable_flags.cached_blend != enable_flags.cached_blend) {
       EnableDisable(GL_BLEND, enable_flags.cached_blend);
+    }
     if (prev_state->enable_flags.cached_cull_face !=
-        enable_flags.cached_cull_face)
+        enable_flags.cached_cull_face) {
       EnableDisable(GL_CULL_FACE, enable_flags.cached_cull_face);
+    }
     if (prev_state->enable_flags.cached_depth_test !=
-        enable_flags.cached_depth_test)
+        enable_flags.cached_depth_test) {
       EnableDisable(GL_DEPTH_TEST, enable_flags.cached_depth_test);
-    if (prev_state->enable_flags.cached_dither != enable_flags.cached_dither)
+    }
+    if (prev_state->enable_flags.cached_dither != enable_flags.cached_dither) {
       EnableDisable(GL_DITHER, enable_flags.cached_dither);
+    }
     if (prev_state->enable_flags.cached_polygon_offset_fill !=
-        enable_flags.cached_polygon_offset_fill)
+        enable_flags.cached_polygon_offset_fill) {
       EnableDisable(GL_POLYGON_OFFSET_FILL,
                     enable_flags.cached_polygon_offset_fill);
+    }
     if (prev_state->enable_flags.cached_sample_alpha_to_coverage !=
-        enable_flags.cached_sample_alpha_to_coverage)
+        enable_flags.cached_sample_alpha_to_coverage) {
       EnableDisable(GL_SAMPLE_ALPHA_TO_COVERAGE,
                     enable_flags.cached_sample_alpha_to_coverage);
+    }
     if (prev_state->enable_flags.cached_sample_coverage !=
-        enable_flags.cached_sample_coverage)
+        enable_flags.cached_sample_coverage) {
       EnableDisable(GL_SAMPLE_COVERAGE, enable_flags.cached_sample_coverage);
+    }
     if (prev_state->enable_flags.cached_scissor_test !=
-        enable_flags.cached_scissor_test)
+        enable_flags.cached_scissor_test) {
       EnableDisable(GL_SCISSOR_TEST, enable_flags.cached_scissor_test);
+    }
     if (prev_state->enable_flags.cached_stencil_test !=
-        enable_flags.cached_stencil_test)
+        enable_flags.cached_stencil_test) {
       EnableDisable(GL_STENCIL_TEST, enable_flags.cached_stencil_test);
+    }
+    if (feature_info_->IsES3Capable()) {
+      if (prev_state->enable_flags.cached_rasterizer_discard !=
+          enable_flags.cached_rasterizer_discard) {
+        EnableDisable(GL_RASTERIZER_DISCARD,
+                      enable_flags.cached_rasterizer_discard);
+      }
+    }
   } else {
     EnableDisable(GL_BLEND, enable_flags.cached_blend);
     EnableDisable(GL_CULL_FACE, enable_flags.cached_cull_face);
@@ -173,6 +191,10 @@
     EnableDisable(GL_SAMPLE_COVERAGE, enable_flags.cached_sample_coverage);
     EnableDisable(GL_SCISSOR_TEST, enable_flags.cached_scissor_test);
     EnableDisable(GL_STENCIL_TEST, enable_flags.cached_stencil_test);
+    if (feature_info_->IsES3Capable()) {
+      EnableDisable(GL_RASTERIZER_DISCARD,
+                    enable_flags.cached_rasterizer_discard);
+    }
   }
 }
 
@@ -358,6 +380,8 @@
       return enable_flags.scissor_test;
     case GL_STENCIL_TEST:
       return enable_flags.stencil_test;
+    case GL_RASTERIZER_DISCARD:
+      return enable_flags.rasterizer_discard;
     default:
       NOTREACHED();
       return false;
@@ -700,6 +724,12 @@
         params[0] = static_cast<GLint>(enable_flags.stencil_test);
       }
       return true;
+    case GL_RASTERIZER_DISCARD:
+      *num_written = 1;
+      if (params) {
+        params[0] = static_cast<GLint>(enable_flags.rasterizer_discard);
+      }
+      return true;
     default:
       return false;
   }
@@ -1037,6 +1067,12 @@
         params[0] = static_cast<GLfloat>(enable_flags.stencil_test);
       }
       return true;
+    case GL_RASTERIZER_DISCARD:
+      *num_written = 1;
+      if (params) {
+        params[0] = static_cast<GLfloat>(enable_flags.rasterizer_discard);
+      }
+      return true;
     default:
       return false;
   }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 319e37d..0886768 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -1641,11 +1641,16 @@
   // Validates the program and location for a glGetUniform call and returns
   // a SizeResult setup to receive the result. Returns true if glGetUniform
   // should be called.
-  bool GetUniformSetup(
-      GLuint program, GLint fake_location,
-      uint32 shm_id, uint32 shm_offset,
-      error::Error* error, GLint* real_location, GLuint* service_id,
-      void** result, GLenum* result_type);
+  bool GetUniformSetup(GLuint program,
+                       GLint fake_location,
+                       uint32 shm_id,
+                       uint32 shm_offset,
+                       error::Error* error,
+                       GLint* real_location,
+                       GLuint* service_id,
+                       void** result,
+                       GLenum* result_type,
+                       GLsizei* result_size);
 
   void MaybeExitOnContextLost();
   bool WasContextLost() override;
@@ -9602,11 +9607,16 @@
   return error::kNoError;
 }
 
-bool GLES2DecoderImpl::GetUniformSetup(
-    GLuint program_id, GLint fake_location,
-    uint32 shm_id, uint32 shm_offset,
-    error::Error* error, GLint* real_location,
-    GLuint* service_id, void** result_pointer, GLenum* result_type) {
+bool GLES2DecoderImpl::GetUniformSetup(GLuint program_id,
+                                       GLint fake_location,
+                                       uint32 shm_id,
+                                       uint32 shm_offset,
+                                       error::Error* error,
+                                       GLint* real_location,
+                                       GLuint* service_id,
+                                       void** result_pointer,
+                                       GLenum* result_type,
+                                       GLsizei* result_size) {
   DCHECK(error);
   DCHECK(service_id);
   DCHECK(result_pointer);
@@ -9658,6 +9668,7 @@
     return false;
   }
   result->size = size;
+  *result_size = size;
   *result_type = type;
   return true;
 }
@@ -9670,12 +9681,13 @@
   GLint fake_location = c.location;
   GLuint service_id;
   GLenum result_type;
+  GLsizei result_size;
   GLint real_location = -1;
   Error error;
   void* result;
-  if (GetUniformSetup(
-      program, fake_location, c.params_shm_id, c.params_shm_offset,
-      &error, &real_location, &service_id, &result, &result_type)) {
+  if (GetUniformSetup(program, fake_location, c.params_shm_id,
+                      c.params_shm_offset, &error, &real_location, &service_id,
+                      &result, &result_type, &result_size)) {
     glGetUniformiv(
         service_id, real_location,
         static_cast<cmds::GetUniformiv::Result*>(result)->GetData());
@@ -9695,13 +9707,14 @@
   typedef cmds::GetUniformfv::Result Result;
   Result* result;
   GLenum result_type;
-  if (GetUniformSetup(
-      program, fake_location, c.params_shm_id, c.params_shm_offset,
-      &error, &real_location, &service_id,
-      reinterpret_cast<void**>(&result), &result_type)) {
+  GLsizei result_size;
+  if (GetUniformSetup(program, fake_location, c.params_shm_id,
+                      c.params_shm_offset, &error, &real_location, &service_id,
+                      reinterpret_cast<void**>(&result), &result_type,
+                      &result_size)) {
     if (result_type == GL_BOOL || result_type == GL_BOOL_VEC2 ||
         result_type == GL_BOOL_VEC3 || result_type == GL_BOOL_VEC4) {
-      GLsizei num_values = result->GetNumResults();
+      GLsizei num_values = result_size / sizeof(Result::Type);
       scoped_ptr<GLint[]> temp(new GLint[num_values]);
       glGetUniformiv(service_id, real_location, temp.get());
       GLfloat* dst = result->GetData();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 3c353c4..9a27c2f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -4871,6 +4871,14 @@
         framebuffer_state_.clear_state_dirty = true;
       }
       return false;
+    case GL_RASTERIZER_DISCARD:
+      state_.enable_flags.rasterizer_discard = enabled;
+      if (state_.enable_flags.cached_rasterizer_discard != enabled ||
+          state_.ignore_cached_state) {
+        state_.enable_flags.cached_rasterizer_discard = enabled;
+        return true;
+      }
+      return false;
     default:
       NOTREACHED();
       return false;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
index 0aca4df..b76e7bf 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h
@@ -12,7 +12,7 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_0_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_0_AUTOGEN_H_
 
-void GLES2DecoderTestBase::SetupInitCapabilitiesExpectations() {
+void GLES2DecoderTestBase::SetupInitCapabilitiesExpectations(bool es3_capable) {
   ExpectEnableDisable(GL_BLEND, false);
   ExpectEnableDisable(GL_CULL_FACE, false);
   ExpectEnableDisable(GL_DEPTH_TEST, false);
@@ -22,6 +22,9 @@
   ExpectEnableDisable(GL_SAMPLE_COVERAGE, false);
   ExpectEnableDisable(GL_SCISSOR_TEST, false);
   ExpectEnableDisable(GL_STENCIL_TEST, false);
+  if (es3_capable) {
+    ExpectEnableDisable(GL_RASTERIZER_DISCARD, false);
+  }
 }
 
 void GLES2DecoderTestBase::SetupInitStateExpectations() {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index c75d7f5..119096b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -338,7 +338,7 @@
           max_viewport_dims, max_viewport_dims + arraysize(max_viewport_dims)))
       .RetiresOnSaturation();
 
-  SetupInitCapabilitiesExpectations();
+  SetupInitCapabilitiesExpectations(group_->feature_info()->IsES3Capable());
   SetupInitStateExpectations();
 
   EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
index 18e7422..0ca537a 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h
@@ -229,7 +229,7 @@
       GLuint vertex_shader_client_id, GLuint vertex_shader_service_id,
       GLuint fragment_shader_client_id, GLuint fragment_shader_service_id);
 
-  void SetupInitCapabilitiesExpectations();
+  void SetupInitCapabilitiesExpectations(bool es3_capable);
   void SetupInitStateExpectations();
   void ExpectEnableDisable(GLenum cap, bool enable);
 
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 ee63b3a..22c5be7 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -88,6 +88,10 @@
     GL_STENCIL_TEST,
 };
 
+static const GLenum valid_capability_table_es3[] = {
+    GL_RASTERIZER_DISCARD,
+};
+
 static const GLenum valid_cmp_function_table[] = {
     GL_NEVER,
     GL_LESS,
@@ -246,6 +250,7 @@
     GL_SAMPLE_COVERAGE,
     GL_SCISSOR_TEST,
     GL_STENCIL_TEST,
+    GL_RASTERIZER_DISCARD,
 };
 
 static const GLenum valid_get_max_index_type_table[] = {
@@ -936,6 +941,8 @@
 void Validators::UpdateValuesES3() {
   buffer_target.AddValues(valid_buffer_target_table_es3,
                           arraysize(valid_buffer_target_table_es3));
+  capability.AddValues(valid_capability_table_es3,
+                       arraysize(valid_capability_table_es3));
   pixel_type.AddValues(valid_pixel_type_table_es3,
                        arraysize(valid_pixel_type_table_es3));
   texture_bind_target.AddValues(valid_texture_bind_target_table_es3,
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn
index 6139153..9304695 100644
--- a/gpu/config/BUILD.gn
+++ b/gpu/config/BUILD.gn
@@ -35,7 +35,6 @@
     "gpu_info_collector_ozone.cc",
     "gpu_info_collector_win.cc",
     "gpu_info_collector_x11.cc",
-    "gpu_performance_stats.h",
     "gpu_test_config.cc",
     "gpu_test_config.h",
     "gpu_test_expectations_parser.cc",
diff --git a/gpu/config/gpu_blacklist_unittest.cc b/gpu/config/gpu_blacklist_unittest.cc
index c9fa415..1a3deb6 100644
--- a/gpu/config/gpu_blacklist_unittest.cc
+++ b/gpu/config/gpu_blacklist_unittest.cc
@@ -65,9 +65,6 @@
     gpu_info_.machine_model_version = "7.1";
     gpu_info_.gl_vendor = "NVIDIA Corporation";
     gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
-    gpu_info_.performance_stats.graphics = 5.0;
-    gpu_info_.performance_stats.gaming = 5.0;
-    gpu_info_.performance_stats.overall = 5.0;
   }
 
   void TearDown() override {}
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
index c95ef9f..3dd1d92 100644
--- a/gpu/config/gpu_control_list.cc
+++ b/gpu/config/gpu_control_list.cc
@@ -1204,18 +1204,6 @@
       !gl_reset_notification_strategy_info_->Contains(
           gpu_info.gl_reset_notification_strategy))
     return false;
-  if (perf_graphics_info_.get() != NULL &&
-      (gpu_info.performance_stats.graphics == 0.0 ||
-       !perf_graphics_info_->Contains(gpu_info.performance_stats.graphics)))
-    return false;
-  if (perf_gaming_info_.get() != NULL &&
-      (gpu_info.performance_stats.gaming == 0.0 ||
-       !perf_gaming_info_->Contains(gpu_info.performance_stats.gaming)))
-    return false;
-  if (perf_overall_info_.get() != NULL &&
-      (gpu_info.performance_stats.overall == 0.0 ||
-       !perf_overall_info_->Contains(gpu_info.performance_stats.overall)))
-    return false;
   if (!machine_model_name_list_.empty()) {
     if (gpu_info.machine_model_name.empty())
       return false;
diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc
index 5587446..0c31e20 100644
--- a/gpu/config/gpu_control_list_entry_unittest.cc
+++ b/gpu/config/gpu_control_list_entry_unittest.cc
@@ -59,9 +59,6 @@
     gpu_info_.gl_version = "2.1 NVIDIA-8.24.11 310.90.9b01";
     gpu_info_.gl_vendor = "NVIDIA Corporation";
     gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
-    gpu_info_.performance_stats.graphics = 5.0;
-    gpu_info_.performance_stats.gaming = 5.0;
-    gpu_info_.performance_stats.overall = 5.0;
   }
 
  protected:
@@ -610,61 +607,6 @@
       GpuControlList::kOsMacosx, "10.9", gpu_info));
 }
 
-TEST_F(GpuControlListEntryTest, PerfGraphicsEntry) {
-  const std::string json = LONG_STRING_CONST(
-      {
-        "id": 1,
-        "perf_graphics": {
-          "op": "<",
-          "value": "6.0"
-        },
-        "features": [
-          "test_feature_0"
-        ]
-      }
-  );
-  ScopedEntry entry(GetEntryFromString(json));
-  EXPECT_TRUE(entry.get() != NULL);
-  EXPECT_TRUE(entry->Contains(GpuControlList::kOsWin, "10.6", gpu_info()));
-}
-
-TEST_F(GpuControlListEntryTest, PerfGamingEntry) {
-  const std::string json = LONG_STRING_CONST(
-      {
-        "id": 1,
-        "perf_graphics": {
-          "op": "<=",
-          "value": "4.0"
-        },
-        "features": [
-          "test_feature_0"
-        ]
-      }
-  );
-  ScopedEntry entry(GetEntryFromString(json));
-  EXPECT_TRUE(entry.get() != NULL);
-  EXPECT_FALSE(entry->Contains(GpuControlList::kOsWin, "10.6", gpu_info()));
-}
-
-TEST_F(GpuControlListEntryTest, PerfOverallEntry) {
-  const std::string json = LONG_STRING_CONST(
-      {
-        "id": 1,
-        "perf_overall": {
-          "op": "between",
-          "value": "1.0",
-          "value2": "9.0"
-        },
-        "features": [
-          "test_feature_0"
-        ]
-      }
-  );
-  ScopedEntry entry(GetEntryFromString(json));
-  EXPECT_TRUE(entry.get() != NULL);
-  EXPECT_TRUE(entry->Contains(GpuControlList::kOsWin, "10.6", gpu_info()));
-}
-
 TEST_F(GpuControlListEntryTest, DisabledEntry) {
   const std::string json = LONG_STRING_CONST(
       {
diff --git a/gpu/config/gpu_control_list_format.txt b/gpu/config/gpu_control_list_format.txt
index c897cb6..88c6f29 100644
--- a/gpu/config/gpu_control_list_format.txt
+++ b/gpu/config/gpu_control_list_format.txt
@@ -44,24 +44,21 @@
 // 12. "gl_vendor" is a string pattern.
 // 13. "gl_renderer" is a string pattern.
 // 14. "gl_extensions" is a string pattern.
-// 15. "perf_graphics" is a FLOAT structure (defined below).
-// 16. "perf_gaming" is a FLOAT structure (defined below).
-// 17. "perf_overall" is a FLOAT structure (defined below).
-// 18. "machine_model_name" is an array of string patterns.
-// 19. "machine_model_version" is a VERSION structure (defined below).
-// 20. "gpu_count" is a INT structure (defined below).
-// 21  "cpu_info" is a string pattern.
-// 22. "exceptions" is a list of entries.
-// 23. "features" is a list of gpu control list options, which can be
+// 15. "machine_model_name" is an array of string patterns.
+// 16. "machine_model_version" is a VERSION structure (defined below).
+// 17. "gpu_count" is a INT structure (defined below).
+// 18. "cpu_info" is a string pattern.
+// 19. "exceptions" is a list of entries.
+// 20. "features" is a list of gpu control list options, which can be
 //     configured by a specific list. See its *_json.cc file for a list of
 //     supported features. This field is mandatory.
-// 24. "description" has the description of the entry.
-// 25. "webkit_bugs" is an array of associated webkit bug numbers.
-// 26. "cr_bugs" is an array of associated webkit bug numbers.
-// 27. "disabled" is a boolean. If it is present, the entry will be skipped.
+// 21. "description" has the description of the entry.
+// 22. "webkit_bugs" is an array of associated webkit bug numbers.
+// 23. "cr_bugs" is an array of associated webkit bug numbers.
+// 24. "disabled" is a boolean. If it is present, the entry will be skipped.
 //     This can not be used in exceptions.
-// 28. "direct_rendering" is a boolean. If present, this will filter on whether
-// the GL contexts are direct or indirect based on the value.
+// 25. "direct_rendering" is a boolean. If present, this will filter on whether
+//     the GL contexts are direct or indirect based on the value.
 //
 // VERSION includes "op", "style", "value", and "value2".  "op" can be any of
 // the following values: "=", "<", "<=", ">", ">=", "any", "between".  "style"
diff --git a/gpu/config/gpu_control_list_unittest.cc b/gpu/config/gpu_control_list_unittest.cc
index f968183..716722c 100644
--- a/gpu/config/gpu_control_list_unittest.cc
+++ b/gpu/config/gpu_control_list_unittest.cc
@@ -57,9 +57,6 @@
     gpu_info_.machine_model_version = "7.1";
     gpu_info_.gl_vendor = "NVIDIA Corporation";
     gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
-    gpu_info_.performance_stats.graphics = 5.0;
-    gpu_info_.performance_stats.gaming = 5.0;
-    gpu_info_.performance_stats.overall = 5.0;
   }
 
   void TearDown() override {}
diff --git a/gpu/config/gpu_driver_bug_list_unittest.cc b/gpu/config/gpu_driver_bug_list_unittest.cc
index 33e09a1..4d62c39 100644
--- a/gpu/config/gpu_driver_bug_list_unittest.cc
+++ b/gpu/config/gpu_driver_bug_list_unittest.cc
@@ -36,9 +36,6 @@
     gpu_info_.machine_model_version = "7.1";
     gpu_info_.gl_vendor = "NVIDIA Corporation";
     gpu_info_.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine";
-    gpu_info_.performance_stats.graphics = 5.0;
-    gpu_info_.performance_stats.gaming = 5.0;
-    gpu_info_.performance_stats.overall = 5.0;
   }
 
   void TearDown() override {}
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 6ceb7fc..2cba5d8 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -90,7 +90,6 @@
     std::string gl_ws_extensions;
     uint32 gl_reset_notification_strategy;
     bool can_lose_context;
-    GpuPerformanceStats performance_stats;
     bool software_rendering;
     bool direct_rendering;
     bool sandboxed;
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
index 7c92d3a..e5bef23 100644
--- a/gpu/config/gpu_info.h
+++ b/gpu/config/gpu_info.h
@@ -16,7 +16,6 @@
 #include "base/version.h"
 #include "build/build_config.h"
 #include "gpu/config/dx_diag_node.h"
-#include "gpu/config/gpu_performance_stats.h"
 #include "gpu/gpu_export.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -180,9 +179,6 @@
   // semantics are available.
   bool can_lose_context;
 
-  // By default all values are 0.
-  GpuPerformanceStats performance_stats;
-
   bool software_rendering;
 
   // Whether the driver uses direct rendering. True on most platforms, false on
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc
index e8c8f2f..cc935ea 100644
--- a/gpu/config/gpu_info_collector_win.cc
+++ b/gpu/config/gpu_info_collector_win.cc
@@ -63,116 +63,6 @@
   return static_cast<float>(score);
 }
 
-GpuPerformanceStats RetrieveGpuPerformanceStats() {
-  TRACE_EVENT0("gpu", "RetrieveGpuPerformanceStats");
-
-  // If the user re-runs the assessment without restarting, the COM API
-  // returns WINSAT_ASSESSMENT_STATE_NOT_AVAILABLE. Because of that and
-  // http://crbug.com/124325, read the assessment result files directly.
-  GpuPerformanceStats stats;
-
-  // Get path to WinSAT results files.
-  wchar_t winsat_results_path[MAX_PATH];
-  DWORD size = ExpandEnvironmentStrings(
-      L"%WinDir%\\Performance\\WinSAT\\DataStore\\",
-      winsat_results_path, MAX_PATH);
-  if (size == 0 || size > MAX_PATH) {
-    LOG(ERROR) << "The path to the WinSAT results is too long: "
-               << size << " chars.";
-    return stats;
-  }
-
-  // Find most recent formal assessment results.
-  base::FileEnumerator file_enumerator(
-      base::FilePath(winsat_results_path),
-      false,  // not recursive
-      base::FileEnumerator::FILES,
-      FILE_PATH_LITERAL("* * Formal.Assessment (*).WinSAT.xml"));
-
-  base::FilePath current_results;
-  for (base::FilePath results = file_enumerator.Next(); !results.empty();
-       results = file_enumerator.Next()) {
-    // The filenames start with the date and time as yyyy-mm-dd hh.mm.ss.xxx,
-    // so the greatest file lexicographically is also the most recent file.
-    if (base::FilePath::CompareLessIgnoreCase(current_results.value(),
-                                              results.value()))
-      current_results = results;
-  }
-
-  std::string current_results_string = current_results.MaybeAsASCII();
-  if (current_results_string.empty())
-    return stats;
-
-  // Get relevant scores from results file. XML schema at:
-  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa969210.aspx
-  XmlReader reader;
-  if (!reader.LoadFile(current_results_string)) {
-    LOG(ERROR) << "Could not open WinSAT results file.";
-    return stats;
-  }
-  // Descend into <WinSAT> root element.
-  if (!reader.SkipToElement() || !reader.Read()) {
-    LOG(ERROR) << "Could not read WinSAT results file.";
-    return stats;
-  }
-
-  // Search for <WinSPR> element containing the results.
-  do {
-    if (reader.NodeName() == "WinSPR")
-      break;
-  } while (reader.Next());
-  // Descend into <WinSPR> element.
-  if (!reader.Read()) {
-    LOG(ERROR) << "Could not find WinSPR element in results file.";
-    return stats;
-  }
-
-  // Read scores.
-  for (int depth = reader.Depth(); reader.Depth() == depth; reader.Next()) {
-    std::string node_name = reader.NodeName();
-    if (node_name == "SystemScore")
-      stats.overall = ReadXMLFloatValue(&reader);
-    else if (node_name == "GraphicsScore")
-      stats.graphics = ReadXMLFloatValue(&reader);
-    else if (node_name == "GamingScore")
-      stats.gaming = ReadXMLFloatValue(&reader);
-  }
-
-  if (stats.overall == 0.0)
-    LOG(ERROR) << "Could not read overall score from assessment results.";
-  if (stats.graphics == 0.0)
-    LOG(ERROR) << "Could not read graphics score from assessment results.";
-  if (stats.gaming == 0.0)
-    LOG(ERROR) << "Could not read gaming score from assessment results.";
-
-  return stats;
-}
-
-GpuPerformanceStats RetrieveGpuPerformanceStatsWithHistograms() {
-  base::TimeTicks start_time = base::TimeTicks::Now();
-
-  GpuPerformanceStats stats = RetrieveGpuPerformanceStats();
-
-  UMA_HISTOGRAM_TIMES("GPU.WinSAT.ReadResultsFileTime",
-                      base::TimeTicks::Now() - start_time);
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "GPU.WinSAT.OverallScore2",
-      static_cast<base::HistogramBase::Sample>(stats.overall * 10), 10, 200,
-      50);
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "GPU.WinSAT.GraphicsScore2",
-      static_cast<base::HistogramBase::Sample>(stats.graphics * 10), 10, 200,
-      50);
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "GPU.WinSAT.GamingScore2",
-      static_cast<base::HistogramBase::Sample>(stats.gaming * 10), 10, 200, 50);
-  UMA_HISTOGRAM_BOOLEAN(
-      "GPU.WinSAT.HasResults",
-      stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0);
-
-  return stats;
-}
-
 // Returns the display link driver version or an invalid version if it is
 // not installed.
 Version DisplayLinkVersion() {
@@ -505,8 +395,6 @@
 
   DCHECK(gpu_info);
 
-  gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms();
-
   // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
   HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
   gpu_info->optimus = nvd3d9wrap != NULL;
@@ -618,6 +506,8 @@
 
   MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
 
+  basic_gpu_info->dx_diagnostics_info_state =
+      context_gpu_info.dx_diagnostics_info_state;
   basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
 }
 
diff --git a/gpu/config/gpu_performance_stats.h b/gpu/config/gpu_performance_stats.h
deleted file mode 100644
index 2ade8bf..0000000
--- a/gpu/config/gpu_performance_stats.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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.
-
-#ifndef GPU_CONFIG_GPU_PERFORMANCE_STATS_H_
-#define GPU_CONFIG_GPU_PERFORMANCE_STATS_H_
-
-#include "gpu/gpu_export.h"
-
-namespace gpu {
-
-struct GPU_EXPORT GpuPerformanceStats {
-  GpuPerformanceStats() : graphics(0.f), gaming(0.f), overall(0.f) {}
-
-  float graphics;
-  float gaming;
-  float overall;
-};
-
-}  // namespace gpu
-
-#endif  // GPU_CONFIG_GPU_PERFORMANCE_STATS_H_
-
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
index 95d828d..bd846a6 100644
--- a/gpu/config/software_rendering_list_json.cc
+++ b/gpu/config/software_rendering_list_json.cc
@@ -18,7 +18,7 @@
 {
   "name": "software rendering list",
   // Please update the version number whenever you change this file.
-  "version": "9.19",
+  "version": "10.0",
   "entries": [
     {
       "id": 1,
@@ -336,32 +336,6 @@
       ]
     },
     {
-      "id": 32,
-      "description": "Accelerated 2d canvas is disabled on Windows systems with low perf stats",
-      "cr_bugs": [116350, 151500],
-      "os": {
-        "type": "win"
-      },
-      "perf_overall": {
-        "op": "<",
-        "value": "3.5"
-      },
-      "exceptions": [
-        {
-          "perf_gaming": {
-            "op": ">",
-            "value": "3.5"
-          }
-        },
-        {
-          "cpu_info": "(?i).*Atom.*"
-        }
-      ],
-      "features": [
-        "accelerated_2d_canvas"
-      ]
-    },
-    {
       "id": 34,
       "description": "S3 Trio (used in Virtual PC) is not compatible",
       "cr_bugs": [119948],
diff --git a/gpu/gpu_config.gypi b/gpu/gpu_config.gypi
index f1915f3..2b94403 100644
--- a/gpu/gpu_config.gypi
+++ b/gpu/gpu_config.gypi
@@ -36,7 +36,6 @@
     'config/gpu_info_collector_ozone.cc',
     'config/gpu_info_collector_win.cc',
     'config/gpu_info_collector_x11.cc',
-    'config/gpu_performance_stats.h',
     'config/gpu_test_config.cc',
     'config/gpu_test_config.h',
     'config/gpu_test_expectations_parser.cc',
diff --git a/gpu/gpu_unittests.isolate b/gpu/gpu_unittests.isolate
index b12c5cb..7ad1b79 100644
--- a/gpu/gpu_unittests.isolate
+++ b/gpu/gpu_unittests.isolate
@@ -59,5 +59,6 @@
   ],
   'includes': [
     '../base/base.isolate',
+    '../third_party/angle/angle.isolate',
   ],
 }
diff --git a/gpu/perftests/texture_upload_perftest.cc b/gpu/perftests/texture_upload_perftest.cc
index 89e901a..c14a151 100644
--- a/gpu/perftests/texture_upload_perftest.cc
+++ b/gpu/perftests/texture_upload_perftest.cc
@@ -20,14 +20,15 @@
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_enums.h"
 #include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_version_info.h"
 #include "ui/gl/gpu_timing.h"
 #include "ui/gl/scoped_make_current.h"
 
 namespace gpu {
 namespace {
 
-const int kUploadPerfWarmupRuns = 10;
-const int kUploadPerfTestRuns = 100;
+const int kUploadPerfWarmupRuns = 5;
+const int kUploadPerfTestRuns = 30;
 
 #define SHADER(Src) #Src
 
@@ -44,9 +45,12 @@
     v_texCoord = a_texCoord;
   }
 );
-const char kFragmentShader[] =
+const char kShaderDefaultFloatPrecision[] =
 SHADER(
   precision mediump float;
+);
+const char kFragmentShader[] =
+SHADER(
   uniform sampler2D a_texture;
   varying vec2 v_texCoord;
   void main() {
@@ -55,8 +59,8 @@
 );
 // clang-format on
 
-void CheckNoGlError() {
-  CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+void CheckNoGlError(const std::string& msg) {
+  CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()) << " " << msg;
 }
 
 // Utility function to compile a shader from a string.
@@ -87,6 +91,24 @@
   return format == GL_RGBA ? 4 : 1;
 }
 
+GLenum GLFormatToInternalFormat(GLenum format) {
+  return format == GL_RED ? GL_R8 : format;
+}
+
+GLenum GLFormatToStorageFormat(GLenum format) {
+  switch (format) {
+    case GL_RGBA:
+      return GL_RGBA8;
+    case GL_LUMINANCE:
+      return GL_LUMINANCE8;
+    case GL_RED:
+      return GL_R8;
+    default:
+      NOTREACHED();
+  }
+  return 0;
+}
+
 void GenerateTextureData(const gfx::Size& size,
                          int bytes_per_pixel,
                          const int seed,
@@ -123,7 +145,7 @@
         case GL_LUMINANCE:  // (L_t, L_t, L_t, 1)
           expected[1] = pixels[pixels_index];
           expected[2] = pixels[pixels_index];
-        case GL_RED_EXT:  // (R_t, 0, 0, 1)n
+        case GL_RED:  // (R_t, 0, 0, 1)
           expected[0] = pixels[pixels_index];
           expected[3] = 255;
           break;
@@ -187,7 +209,12 @@
     // 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);
-    fragment_shader_ = LoadShader(GL_FRAGMENT_SHADER, kFragmentShader);
+
+    bool is_gles = gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2;
+    fragment_shader_ = LoadShader(
+        GL_FRAGMENT_SHADER,
+        base::StringPrintf("%s%s", is_gles ? kShaderDefaultFloatPrecision : "",
+                           kFragmentShader).c_str());
     program_object_ = glCreateProgram();
     CHECK_NE(0u, program_object_);
 
@@ -219,7 +246,12 @@
                           reinterpret_cast<void*>(sizeof(GLfloat) * 2));
     glEnableVertexAttribArray(0);
     glEnableVertexAttribArray(1);
-    CheckNoGlError();
+    CheckNoGlError("glEnableVertexAttribArray");
+
+    has_texture_storage_ =
+        gl_context_->GetVersionInfo()->is_es3 ||
+        gl_context_->HasExtension("GL_EXT_texture_storage") ||
+        gl_context_->HasExtension("GL_ARB_texture_storage");
   }
 
   void GenerateVertexBuffer(const gfx::Size& size) {
@@ -241,7 +273,7 @@
       right, top,    1.f, 1.f};
     // clang-format on
     glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
-    CheckNoGlError();
+    CheckNoGlError("glBufferData");
   }
 
   void TearDown() override {
@@ -254,7 +286,7 @@
     glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
     glDeleteFramebuffersEXT(1, &framebuffer_object_);
     glDeleteTextures(1, &color_texture_);
-    CheckNoGlError();
+    CheckNoGlError("glDeleteTextures");
 
     gpu_timing_client_ = nullptr;
     gl_context_ = nullptr;
@@ -262,36 +294,59 @@
   }
 
  protected:
-  GLuint CreateGLTexture() {
+  GLuint CreateGLTexture(const GLenum format,
+                         const gfx::Size& size,
+                         const bool specify_storage) {
     GLuint texture_id = 0;
     glActiveTexture(GL_TEXTURE0);
     glGenTextures(1, &texture_id);
     glBindTexture(GL_TEXTURE_2D, texture_id);
+    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);
+    if (specify_storage) {
+      if (has_texture_storage_) {
+        glTexStorage2DEXT(GL_TEXTURE_2D, 1, GLFormatToStorageFormat(format),
+                          size.width(), size.height());
+        CheckNoGlError("glTexStorage2DEXT");
+      } else {
+        glTexImage2D(GL_TEXTURE_2D, 0, GLFormatToInternalFormat(format),
+                     size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE,
+                     nullptr);
+        CheckNoGlError("glTexImage2D");
+      }
+    }
     return texture_id;
   }
 
   void UploadTexture(GLuint texture_id,
                      const gfx::Size& size,
                      const std::vector<uint8>& pixels,
-                     GLenum format) {
-    glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0,
-                 format, GL_UNSIGNED_BYTE, &pixels[0]);
-    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();
+                     GLenum format,
+                     const bool subimage) {
+    if (subimage) {
+      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(),
+                      format, GL_UNSIGNED_BYTE, &pixels[0]);
+      CheckNoGlError("glTexSubImage2D");
+    } else {
+      glTexImage2D(GL_TEXTURE_2D, 0, GLFormatToInternalFormat(format),
+                   size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE,
+                   &pixels[0]);
+      CheckNoGlError("glTexImage2D");
+    }
   }
 
   // Upload and draw on the offscren surface.
   // Return a list of pair. Each pair describe a gl operation and the wall
   // time elapsed in milliseconds.
-  std::vector<Measurement> UploadAndDraw(const gfx::Size& size,
+  std::vector<Measurement> UploadAndDraw(GLuint texture_id,
+                                         const gfx::Size& size,
                                          const std::vector<uint8>& pixels,
-                                         const GLenum format) {
-    GLuint texture_id = CreateGLTexture();
+                                         const GLenum format,
+                                         const bool subimage) {
     MeasurementTimers tex_timers(gpu_timing_client_.get());
-    UploadTexture(texture_id, size, pixels, format);
+    UploadTexture(texture_id, size, pixels, format, subimage);
     tex_timers.Record();
 
     MeasurementTimers draw_timers(gpu_timing_client_.get());
@@ -301,15 +356,13 @@
 
     MeasurementTimers finish_timers(gpu_timing_client_.get());
     glFinish();
-    CheckNoGlError();
+    CheckNoGlError("glFinish");
     finish_timers.Record();
 
-    glDeleteTextures(1, &texture_id);
-
     std::vector<uint8> pixels_rendered(size.GetArea() * 4);
     glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
                  &pixels_rendered[0]);
-    CheckNoGlError();
+    CheckNoGlError("glReadPixels");
     EXPECT_TRUE(
         CompareBufferToRGBABuffer(format, size, pixels, pixels_rendered))
         << "Format is: " << gfx::GLEnums::GetStringEnum(format);
@@ -319,7 +372,8 @@
         gpu_timing_client_->IsAvailable() &&
         gpu_timing_client_->CheckAndResetTimerErrors();
     if (!gpu_timer_errors) {
-      measurements.push_back(tex_timers.GetAsMeasurement("teximage2d"));
+      measurements.push_back(tex_timers.GetAsMeasurement(
+          subimage ? "texsubimage2d" : "teximage2d"));
       measurements.push_back(draw_timers.GetAsMeasurement("drawarrays"));
       measurements.push_back(finish_timers.GetAsMeasurement("finish"));
     }
@@ -327,14 +381,16 @@
   }
 
   void RunUploadAndDrawMultipleTimes(const gfx::Size& size,
-                                     const GLenum format) {
+                                     const GLenum format,
+                                     const bool subimage) {
     std::vector<uint8> pixels;
     base::SmallMap<std::map<std::string, Measurement>>
         aggregates;  // indexed by name
     int successful_runs = 0;
+    GLuint texture_id = CreateGLTexture(format, size, subimage);
     for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) {
       GenerateTextureData(size, GLFormatBytePerPixel(format), i + 1, &pixels);
-      auto run = UploadAndDraw(size, pixels, format);
+      auto run = UploadAndDraw(texture_id, size, pixels, format, subimage);
       if (i < kUploadPerfWarmupRuns || !run.size()) {
         continue;
       }
@@ -345,8 +401,14 @@
         aggregate.Increment(measurement);
       }
     }
+    glDeleteTextures(1, &texture_id);
+
     std::string graph_name = base::StringPrintf(
         "%d_%s", size.width(), gfx::GLEnums::GetStringEnum(format).c_str());
+    if (subimage) {
+      graph_name += "_sub";
+    }
+
     if (successful_runs) {
       for (const auto& entry : aggregates) {
         const auto m = entry.second.Divide(successful_runs);
@@ -370,33 +432,41 @@
   GLint sampler_location_ = -1;
   GLint translation_location_ = -1;
   GLuint vertex_buffer_ = 0;
+
+  bool has_texture_storage_ = false;
 };
 
 // Perf test that generates, uploads and draws a texture on a surface repeatedly
 // and prints out aggregated measurements for all the runs.
-TEST_F(TextureUploadPerfTest, glTexImage2d) {
+TEST_F(TextureUploadPerfTest, upload) {
   int sizes[] = {21, 128, 256, 512, 1024};
   std::vector<GLenum> formats;
   formats.push_back(GL_RGBA);
-  // Used by default for ResourceProvider::yuv_resource_format_.
-  formats.push_back(GL_LUMINANCE);
+
+  if (!gl_context_->GetVersionInfo()->is_es3) {
+    // Used by default for ResourceProvider::yuv_resource_format_.
+    formats.push_back(GL_LUMINANCE);
+  }
 
   ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
-  bool has_texture_rg = gl_context_->HasExtension("GL_EXT_texture_rg") ||
-                        gl_context_->HasExtension("GL_ARB_texture_rg");
+  const bool has_texture_rg = gl_context_->GetVersionInfo()->is_es3 ||
+                              gl_context_->HasExtension("GL_EXT_texture_rg") ||
+                              gl_context_->HasExtension("GL_ARB_texture_rg");
 
   if (has_texture_rg) {
     // Used as ResourceProvider::yuv_resource_format_ if
     // {ARB,EXT}_texture_rg are available.
-    formats.push_back(GL_RED_EXT);
+    formats.push_back(GL_RED);
   }
+
   for (int side : sizes) {
     ASSERT_GE(fbo_size_.width(), side);
     ASSERT_GE(fbo_size_.height(), side);
     gfx::Size size(side, side);
     GenerateVertexBuffer(size);
     for (GLenum format : formats) {
-      RunUploadAndDrawMultipleTimes(size, format);
+      RunUploadAndDrawMultipleTimes(size, format, true);  // use glTexSubImage2D
+      RunUploadAndDrawMultipleTimes(size, format, false);  // use glTexImage2D
     }
   }
 }
@@ -425,12 +495,12 @@
                                 gfx::Vector2dF(1.f, 0.f),
                                 gfx::Vector2dF(0.f, 1.f),
                                 gfx::Vector2dF(1.f, 1.f)};
-  GLuint texture_id = CreateGLTexture();
+  GLuint texture_id = CreateGLTexture(GL_RGBA, texture_size, true);
 
   MeasurementTimers upload_and_draw_timers(gpu_timing_client_.get());
 
   for (int i = 0; i < 4; ++i) {
-    UploadTexture(texture_id, texture_size, pixels[i % 4], GL_RGBA);
+    UploadTexture(texture_id, texture_size, pixels[i % 4], GL_RGBA, true);
     DCHECK_NE(-1, translation_location_);
     glUniform2f(translation_location_, positions[i % 4].x(),
                 positions[i % 4].y());
@@ -444,7 +514,7 @@
   upload_and_draw_timers.Record();
   MeasurementTimers finish_timers(gpu_timing_client_.get());
   glFinish();
-  CheckNoGlError();
+  CheckNoGlError("glFinish");
   finish_timers.Record();
 
   glDeleteTextures(1, &texture_id);
@@ -455,7 +525,7 @@
                  texture_size.height() * positions[i].y(), texture_size.width(),
                  texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE,
                  &pixels_rendered[0]);
-    CheckNoGlError();
+    CheckNoGlError("glReadPixels");
     ASSERT_EQ(pixels[i].size(), pixels_rendered.size());
     EXPECT_EQ(pixels[i], pixels_rendered);
   }
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 2357a7f..14b91ef 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -8,6 +8,9 @@
 if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
+if (current_cpu == "mipsel" || current_cpu == "mips64el") {
+  import("//build/config/mips.gni")
+}
 
 skia_support_gpu = !is_ios
 skia_support_pdf = !is_ios && (enable_basic_printing || enable_print_preview)
@@ -36,26 +39,30 @@
                 [ "//third_party/skia/gyp/core.gypi" ])
 
 # The list of Skia gpu sources that are to be set for chromium.
-gypi_skia_gpu =
-    exec_script("//build/gypi_to_gn.py",
-                [
-                  rebase_path("//third_party/skia/gyp/gpu.gypi"),
-                  "--replace=<(skia_include_path)=//third_party/skia/include",
-                  "--replace=<(skia_src_path)=//third_party/skia/src",
-                ],
-                "scope",
-                [ "//third_party/skia/gyp/gpu.gypi" ])
+if (skia_support_gpu) {
+  gypi_skia_gpu =
+      exec_script("//build/gypi_to_gn.py",
+                  [
+                    rebase_path("//third_party/skia/gyp/gpu.gypi"),
+                    "--replace=<(skia_include_path)=//third_party/skia/include",
+                    "--replace=<(skia_src_path)=//third_party/skia/src",
+                  ],
+                  "scope",
+                  [ "//third_party/skia/gyp/gpu.gypi" ])
+}
 
 # The list of Skia pdf sources that are to be set for chromium.
-gypi_skia_pdf =
-    exec_script("//build/gypi_to_gn.py",
-                [
-                  rebase_path("//third_party/skia/gyp/pdf.gypi"),
-                  "--replace=<(skia_include_path)=//third_party/skia/include",
-                  "--replace=<(skia_src_path)=//third_party/skia/src",
-                ],
-                "scope",
-                [ "//third_party/skia/gyp/pdf.gypi" ])
+if (skia_support_pdf) {
+  gypi_skia_pdf =
+      exec_script("//build/gypi_to_gn.py",
+                  [
+                    rebase_path("//third_party/skia/gyp/pdf.gypi"),
+                    "--replace=<(skia_include_path)=//third_party/skia/include",
+                    "--replace=<(skia_src_path)=//third_party/skia/src",
+                  ],
+                  "scope",
+                  [ "//third_party/skia/gyp/pdf.gypi" ])
+}
 
 # The list of Skia effects that are to be set for chromium.
 gypi_skia_effects =
@@ -537,6 +544,7 @@
               [
                 # Chrome-specific.
                 "ext/convolver_SSE2.cc",
+                "ext/convolver_SSE2.h",
               ]
 
     if (is_linux || is_mac) {
@@ -564,7 +572,19 @@
     }
   } else if (current_cpu == "mipsel") {
     cflags += [ "-fomit-frame-pointer" ]
-    sources = gypi_skia_opts.none_sources
+
+    if (mips_dsp_rev >= 1) {
+      sources = gypi_skia_opts.mips_dsp_sources
+      if (mips_dsp_rev >= 2) {
+        sources += [
+          # Chrome-specific.
+          "ext/convolver_mips_dspr2.cc",
+          "ext/convolver_mips_dspr2.h",
+        ]
+      }
+    } else {
+      sources = gypi_skia_opts.none_sources
+    }
   } else {
     assert(false, "Need to port cpu specific stuff from skia_library_opts.gyp")
   }
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 6db5919..e9990e6 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -261,10 +261,18 @@
 #   define SK_IGNORE_ETC1_SUPPORT
 #endif
 
+#ifndef    SK_SUPPORT_LEGACY_BOOL_ONGETINFO
+#   define SK_SUPPORT_LEGACY_BOOL_ONGETINFO
+#endif
+
 #ifndef    SK_IGNORE_GPU_DITHER
 #   define SK_IGNORE_GPU_DITHER
 #endif
 
+#ifndef    SK_SUPPORT_LEGACY_INT_COLORMATRIX
+#   define SK_SUPPORT_LEGACY_INT_COLORMATRIX
+#endif
+
 #ifndef    SK_LEGACY_STROKE_CURVES
 #   define SK_LEGACY_STROKE_CURVES
 #endif
diff --git a/skia/ext/SkDiscardableMemory_chrome.cc b/skia/ext/SkDiscardableMemory_chrome.cc
index 50e7f2e..5bb6928 100644
--- a/skia/ext/SkDiscardableMemory_chrome.cc
+++ b/skia/ext/SkDiscardableMemory_chrome.cc
@@ -4,6 +4,9 @@
 
 #include "SkDiscardableMemory_chrome.h"
 
+#include "base/memory/discardable_memory.h"
+#include "base/memory/discardable_memory_allocator.h"
+
 SkDiscardableMemoryChrome::~SkDiscardableMemoryChrome() {}
 
 bool SkDiscardableMemoryChrome::lock() {
@@ -25,5 +28,6 @@
 
 SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) {
   return new SkDiscardableMemoryChrome(
-      base::DiscardableMemory::CreateLockedMemory(bytes));
+      base::DiscardableMemoryAllocator::GetInstance()
+          ->AllocateLockedDiscardableMemory(bytes));
 }
diff --git a/skia/ext/SkDiscardableMemory_chrome.h b/skia/ext/SkDiscardableMemory_chrome.h
index 50946e6..1be4516 100644
--- a/skia/ext/SkDiscardableMemory_chrome.h
+++ b/skia/ext/SkDiscardableMemory_chrome.h
@@ -5,10 +5,13 @@
 #ifndef SKIA_EXT_SK_DISCARDABLE_MEMORY_CHROME_H_
 #define SKIA_EXT_SK_DISCARDABLE_MEMORY_CHROME_H_
 
-#include "base/memory/discardable_memory.h"
 #include "base/memory/scoped_ptr.h"
 #include "third_party/skia/src/core/SkDiscardableMemory.h"
 
+namespace base {
+class DiscardableMemory;
+}
+
 // This class implements the SkDiscardableMemory interface using
 // base::DiscardableMemory.
 class SK_API SkDiscardableMemoryChrome : public SkDiscardableMemory {
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index 194f382..fb6f7ad 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -249,7 +249,7 @@
         "None", "Low", "Medium", "High"};
     DCHECK_LT(static_cast<size_t>(paint.getFilterQuality()),
               SK_ARRAY_COUNT(gFilterQualityStrings));
-    val->SetString("FilterQuality",
+    val->SetString("FilterLevel",
                    gFilterQualityStrings[paint.getFilterQuality()]);
   }
 
diff --git a/skia/skia.gyp b/skia/skia.gyp
index 16e03a2..109adff 100644
--- a/skia/skia.gyp
+++ b/skia/skia.gyp
@@ -107,14 +107,13 @@
            target_arch != "arm64" and target_arch != "mips64el"', {
           'sources': [
             'ext/convolver_SSE2.cc',
+            'ext/convolver_SSE2.h',
           ],
         }],
-        [ 'target_arch == "mipsel"',{
-          'cflags': [
-            '-fomit-frame-pointer',
-          ],
+        [ 'target_arch == "mipsel" and mips_dsp_rev >= 2',{
           'sources': [
             'ext/convolver_mips_dspr2.cc',
+            'ext/convolver_mips_dspr2.h',
           ],
         }],
       ],
diff --git a/skia/skia_library_opts.gyp b/skia/skia_library_opts.gyp
index cde0e0b..58f036f 100644
--- a/skia/skia_library_opts.gyp
+++ b/skia/skia_library_opts.gyp
@@ -64,11 +64,16 @@
         }],
         [ 'target_arch == "arm"', {
           'conditions': [
+            [ 'arm_version >= 7', {
+              'sources': [ '<@(armv7_sources)' ],
+            }, {  # arm_version < 7
+              'sources': [ '<@(none_sources)' ],
+            }],
             [ 'arm_version >= 7 and (arm_neon == 1 or arm_neon_optional == 1)', {
               'dependencies': [
                 'skia_opts_neon',
               ]
-           }],
+            }],
           ],
           # The assembly uses the frame pointer register (r7 in Thumb/r11 in
           # ARM), the compiler doesn't like that. Explicitly remove the
@@ -83,13 +88,17 @@
             '-fomit-frame-pointer',
           ],
         }],
-        [ 'target_arch == "arm" and arm_version < 7', {
-          'sources': [ '<@(none_sources)' ],
+        [ 'target_arch == "mipsel"',{
+          'cflags': [ '-fomit-frame-pointer' ],
+          'conditions': [
+            [ 'mips_dsp_rev >= 1', {
+              'sources': [ '<@(mips_dsp_sources)' ],
+            }, {  # mips_dsp_rev == 0
+              'sources': [ '<@(none_sources)' ],
+            }],
+          ],
         }],
-        [ 'target_arch == "arm" and arm_version >= 7', {
-          'sources': [ '<@(armv7_sources)' ],
-        }],
-        [ 'target_arch == "mipsel" or target_arch == "mips64el"',{
+        [ 'target_arch == "mips64el"',{
           'cflags': [ '-fomit-frame-pointer' ],
           'sources': [ '<@(none_sources)' ],
         }],
diff --git a/sky/engine/platform/fonts/opentype/OpenTypeSanitizer.cpp b/sky/engine/platform/fonts/opentype/OpenTypeSanitizer.cpp
index 3d628ff..003869e 100644
--- a/sky/engine/platform/fonts/opentype/OpenTypeSanitizer.cpp
+++ b/sky/engine/platform/fonts/opentype/OpenTypeSanitizer.cpp
@@ -48,9 +48,6 @@
     if (m_buffer->size() > maxWebFontSize)
         return nullptr;
 
-    if (RuntimeEnabledFeatures::woff2Enabled())
-        ots::EnableWOFF2();
-
     // 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.
diff --git a/sky/engine/platform/graphics/GraphicsContext.cpp b/sky/engine/platform/graphics/GraphicsContext.cpp
index 92d3f5c..5a8458b 100644
--- a/sky/engine/platform/graphics/GraphicsContext.cpp
+++ b/sky/engine/platform/graphics/GraphicsContext.cpp
@@ -45,6 +45,7 @@
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkData.h"
 #include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkMatrixImageFilter.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/core/SkRRect.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -52,7 +53,6 @@
 #include "third_party/skia/include/effects/SkBlurMaskFilter.h"
 #include "third_party/skia/include/effects/SkCornerPathEffect.h"
 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
-#include "third_party/skia/include/effects/SkMatrixImageFilter.h"
 #include "third_party/skia/include/effects/SkPictureImageFilter.h"
 #include "third_party/skia/include/gpu/GrRenderTarget.h"
 #include "third_party/skia/include/gpu/GrTexture.h"
diff --git a/sky/engine/platform/graphics/filters/FEDropShadow.cpp b/sky/engine/platform/graphics/filters/FEDropShadow.cpp
index dcee91a..5c3dede 100644
--- a/sky/engine/platform/graphics/filters/FEDropShadow.cpp
+++ b/sky/engine/platform/graphics/filters/FEDropShadow.cpp
@@ -111,7 +111,7 @@
     float stdY = filter()->applyVerticalScale(m_stdY);
     Color color = adaptColorToOperatingColorSpace(m_shadowColor.combineWithAlpha(m_shadowOpacity));
     SkImageFilter::CropRect cropRect = getCropRect(builder->cropOffset());
-    return adoptRef(SkDropShadowImageFilter::Create(SkFloatToScalar(dx), SkFloatToScalar(dy), SkFloatToScalar(stdX), SkFloatToScalar(stdY), color.rgb(), input.get(), &cropRect));
+    return adoptRef(SkDropShadowImageFilter::Create(SkFloatToScalar(dx), SkFloatToScalar(dy), SkFloatToScalar(stdX), SkFloatToScalar(stdY), color.rgb(), SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode, input.get(), &cropRect));
 }
 
 
diff --git a/sky/engine/platform/graphics/filters/SkiaImageFilterBuilder.cpp b/sky/engine/platform/graphics/filters/SkiaImageFilterBuilder.cpp
index 3dec62a..b973855 100644
--- a/sky/engine/platform/graphics/filters/SkiaImageFilterBuilder.cpp
+++ b/sky/engine/platform/graphics/filters/SkiaImageFilterBuilder.cpp
@@ -32,11 +32,11 @@
 #include "sky/engine/platform/graphics/filters/SourceGraphic.h"
 #include "sky/engine/platform/graphics/skia/SkiaUtils.h"
 #include "sky/engine/public/platform/WebPoint.h"
+#include "third_party/skia/include/core/SkMatrixImageFilter.h"
 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
 #include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
 #include "third_party/skia/include/effects/SkColorMatrixFilter.h"
 #include "third_party/skia/include/effects/SkDropShadowImageFilter.h"
-#include "third_party/skia/include/effects/SkMatrixImageFilter.h"
 #include "third_party/skia/include/effects/SkTableColorFilter.h"
 
 namespace blink {
diff --git a/testing/android/junit/BUILD.gn b/testing/android/junit/BUILD.gn
index e4cd871..f80d0b5 100644
--- a/testing/android/junit/BUILD.gn
+++ b/testing/android/junit/BUILD.gn
@@ -12,6 +12,7 @@
   deps = [
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
+    "//third_party/robolectric:robolectric_java",
   ]
 }
 
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java b/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java
new file mode 100644
index 0000000..293525e
--- /dev/null
+++ b/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java
@@ -0,0 +1,58 @@
+// 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.
+
+package org.chromium.testing.local;
+
+import org.junit.runners.model.InitializationError;
+
+import org.robolectric.AndroidManifest;
+import org.robolectric.DependencyResolver;
+import org.robolectric.LocalDependencyResolver;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.SdkConfig;
+import org.robolectric.annotation.Config;
+
+import java.io.File;
+
+/**
+ * A custom Robolectric Junit4 Test Runner. This test runner will load the
+ * "real" android jars from a local directory rather than use Maven to fetch
+ * them from the Maven Central repository. Additionally, it will ignore the
+ * API level written in the AndroidManifest as that can cause issues if
+ * robolectric does not support that API level.
+ */
+public class LocalRobolectricTestRunner extends RobolectricTestRunner {
+
+    private static final int ANDROID_API_LEVEL = 18;
+
+    public LocalRobolectricTestRunner(Class<?> testClass) throws InitializationError {
+        super(testClass);
+    }
+
+    @Override
+    protected final DependencyResolver getJarResolver() {
+        String dependencyDir = System.getProperty("robolectric.dependency.dir");
+        if (dependencyDir == null) {
+            throw new IllegalStateException("robolectric.dependency.dir not specified. Make sure"
+                    + " you are setting the robolectric.dependency.dir system property to the"
+                    + " directory containing Robolectric's runtime dependency jars.");
+        }
+        return new LocalDependencyResolver(new File(dependencyDir));
+    }
+
+    @Override
+    protected SdkConfig pickSdkVersion(AndroidManifest appManifest, Config config) {
+        // Pulling from the manifest is dangerous as the apk might target a version of
+        // android that robolectric does not yet support. We still allow the API level to
+        // be overridden with the Config annotation.
+        return config.emulateSdk() < 0
+                ? new SdkConfig(ANDROID_API_LEVEL) : super.pickSdkVersion(null, config);
+    }
+
+    @Override
+    protected int pickReportedSdkVersion(Config config, AndroidManifest appManifest) {
+        return config.reportSdk() < 0
+                ? ANDROID_API_LEVEL : super.pickReportedSdkVersion(config, appManifest);
+    }
+}
\ No newline at end of file
diff --git a/testing/android/junit/junit_test.gyp b/testing/android/junit/junit_test.gyp
index f0f8b8c..16e0048 100644
--- a/testing/android/junit/junit_test.gyp
+++ b/testing/android/junit/junit_test.gyp
@@ -11,6 +11,7 @@
       'dependencies': [
         '../../../third_party/junit/junit.gyp:junit_jar',
         '../../../third_party/mockito/mockito.gyp:mockito_jar',
+        '../../../third_party/robolectric/robolectric.gyp:robolectric_jar'
       ],
       'variables': {
         'src_paths': [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a8730fb..8a47be4 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1128,5 +1128,25 @@
       },
       "url_unittests"
     ]
+  },
+  "Cast Linux": {
+    "gtest_tests": [
+      "base_unittests",
+      "cacheinvalidation_unittests",
+      "cast_media_unittests",
+      "cast_shell_browser_test",
+      "content_unittests",
+      "crypto_unittests",
+      "gpu_unittests",
+      "ipc_tests",
+      "jingle_unittests",
+      "media_unittests",
+      "net_unittests",
+      "sandbox_linux_unittests",
+      "sql_unittests",
+      "sync_unit_tests",
+      "ui_base_unittests",
+      "url_unittests"
+    ]
   }
 }
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 2fd07bd..ec70b5b 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -18,6 +18,11 @@
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py",
         "args": ["gpu_perftests"]
+      },
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests"]
       }
     ]
   },
@@ -31,15 +36,20 @@
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py",
         "args": ["gpu_perftests"]
+      },
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests"]
       }
     ]
   },
   "Android Nexus6 Perf": {
     "scripts": [
       {
-        "name": "gpu_perftests",
+        "name": "cc_perftests",
         "script": "gtest_perf_test.py",
-        "args": ["gpu_perftests"]
+        "args": ["cc_perftests"]
       }
     ]
   },
@@ -53,6 +63,11 @@
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py",
         "args": ["gpu_perftests"]
+      },
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests"]
       }
     ]
   },
@@ -62,6 +77,11 @@
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py",
         "args": ["gpu_perftests"]
+      },
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests"]
       }
     ]
   },
@@ -75,6 +95,11 @@
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py",
         "args": ["gpu_perftests"]
+      },
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests"]
       }
     ]
   },
@@ -92,15 +117,48 @@
         "name": "gpu_perftests",
         "script": "gtest_perf_test.py",
         "args": ["gpu_perftests"]
+      },
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests"]
       }
     ]
   },
   "Mac 10.8 Perf (1)": {
     "scripts": [
+      {
+	"name": "gpu_perftests",
+	"script": "gtest_perf_test.py",
+	"args": ["gpu_perftests", "--test-launcher-print-test-stdio=always"]
+      }
+    ]
+  },
+  "Mac 10.8 Perf (3)": {
+    "scripts": [
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests", "--test-launcher-print-test-stdio=always"]
+      }
     ]
   },
   "Mac 10.9 Perf (1)": {
     "scripts": [
+      {
+	"name": "gpu_perftests",
+	"script": "gtest_perf_test.py",
+	"args": ["gpu_perftests", "--test-launcher-print-test-stdio=always"]
+      }
+    ]
+  },
+  "Mac 10.9 Perf (3)": {
+    "scripts": [
+      {
+        "name": "cc_perftests",
+        "script": "gtest_perf_test.py",
+        "args": ["cc_perftests", "--test-launcher-print-test-stdio=always"]
+      }
     ]
   }
 }
diff --git a/testing/chromoting/integration_tests.gyp b/testing/chromoting/integration_tests.gyp
index 2588d24..b50c3c6 100644
--- a/testing/chromoting/integration_tests.gyp
+++ b/testing/chromoting/integration_tests.gyp
@@ -2,6 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# TODO: Factor out all of the common items across the test targets into a
+# single .gypi file that can be included by each test target.
+
 {
   'conditions': [
     ['archive_chromoting_tests==1', {
@@ -24,10 +27,34 @@
             ['OS=="linux"', {
               'dependencies': [
                 '../../remoting/remoting.gyp:remoting_me2me_host_archive',
+                '../../remoting/webapp/app_remoting/internal/app_remoting_all.gyp:app_remoting_all_apps',
               ],
             }],  # OS=="linux"
           ],
-        },
+        },  # target_name: 'chromoting_integration_tests_run'
+        {
+          'target_name': 'chromoting_multi_machine_example_test',
+          'type': 'none',
+          'dependencies': [
+            '../../chrome/chrome.gyp:browser_tests',
+            '../../remoting/remoting.gyp:remoting_webapp_v1',
+            '../../remoting/remoting.gyp:remoting_webapp_v2',
+          ],
+          'includes': [
+            '../../build/isolate.gypi',
+          ],
+          'sources': [
+            'multi_machine_example/example_test_controller.isolate',
+            'multi_machine_example/example_task.isolate',
+          ],
+          'conditions': [
+            ['OS=="linux"', {
+              'dependencies': [
+                '../../remoting/remoting.gyp:remoting_me2me_host_archive',
+              ],
+            }],  # OS=="linux"
+          ],
+        },  # target_name: 'chromoting_multi_machine_example_test'
       ],
     }],
   ],
diff --git a/testing/chromoting/multi_machine_example/example_task.isolate b/testing/chromoting/multi_machine_example/example_task.isolate
new file mode 100644
index 0000000..710e6da
--- /dev/null
+++ b/testing/chromoting/multi_machine_example/example_task.isolate
@@ -0,0 +1,18 @@
+# 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': [
+    '../chromoting_integration_tests.isolate',
+    '../../legion/legion.isolate',
+    '../../../chrome/chrome.isolate',
+    '../../../remoting/tools/remote_test_helper/remote_test_helper.isolate',
+  ],
+  'variables': {
+    'command': [
+      'python',
+      '../../legion/run_task.py',
+    ],
+  },
+}
diff --git a/testing/chromoting/multi_machine_example/example_test_controller.isolate b/testing/chromoting/multi_machine_example/example_test_controller.isolate
new file mode 100644
index 0000000..957cae4
--- /dev/null
+++ b/testing/chromoting/multi_machine_example/example_test_controller.isolate
@@ -0,0 +1,31 @@
+# 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/legion.isolate',
+  ],
+  'variables': {
+    'command': [
+      'python',
+      'example_test_controller.py',
+      '--commands_file',
+      '../browser_test_commands_linux.txt',
+      '--prod_dir',
+      '<(PRODUCT_DIR)',
+      '--cfg_file',
+      '../../../remoting/tools/internal/test-account-host-config.json',
+      '--me2me_manifest_file',
+      '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_desktop.json',
+      '--it2me_manifest_file',
+      '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_assistance.json',
+      '--user_profile_dir',
+      '/tmp/chromoting_test_profile',
+    ],
+    'files': [
+      'example_test_controller.py',
+      '../../../tools/swarming_client/',
+    ],
+  },
+}
diff --git a/testing/chromoting/multi_machine_example/example_test_controller.py b/testing/chromoting/multi_machine_example/example_test_controller.py
new file mode 100755
index 0000000..2ba2024
--- /dev/null
+++ b/testing/chromoting/multi_machine_example/example_test_controller.py
@@ -0,0 +1,143 @@
+#!/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.
+
+"""The test controller for the chromoting localhost browser_tests.
+
+This test uses the legion framework to setup this controller which will run
+the chromoting_integration_tests on a task machine. This is intended to be an
+example Legion-based test for the chromoting team.
+
+The controller will start a task machine to run browser_tests_launcher on. The
+output of these tests are streamed back to the test controller to be output
+on the controller's stdout and stderr channels. The final test output is then
+read and becomes the final output of the controller, mirroring the test's
+pass/fail result.
+"""
+
+import argparse
+import logging
+import os
+import sys
+import time
+
+# Map the legion directory so we can import the host controller.
+SRC_DIR = os.path.join('..', '..', '..')
+sys.path.append(os.path.join(SRC_DIR, 'testing'))
+from legion import test_controller
+
+
+class ExampleController(test_controller.TestController):
+  """The test controller for the Chromoting browser_tests."""
+
+  def __init__(self):
+    super(ExampleController, self).__init__()
+    self.task = None
+    self.args = None
+
+  def RunTest(self):
+    """Main method to run the test code."""
+    self.ParseArgs()
+    self.CreateTask()
+    self.TestIntegrationTests()
+
+  def CreateBrowserTestsLauncherCommand(self):
+    return [
+        'python',
+        self.TaskAbsPath('../browser_tests_launcher.py'),
+        '--commands_file', self.TaskAbsPath(self.args.commands_file),
+        '--prod_dir', self.TaskAbsPath(self.args.prod_dir),
+        '--cfg_file', self.TaskAbsPath(self.args.cfg_file),
+        '--me2me_manifest_file', self.TaskAbsPath(
+            self.args.me2me_manifest_file),
+        '--it2me_manifest_file', self.TaskAbsPath(
+            self.args.it2me_manifest_file),
+        '--user_profile_dir', self.args.user_profile_dir,
+        ]
+
+  def TaskAbsPath(self, path):
+    """Returns the absolute path to the resource on the task machine.
+
+    Args:
+      path: The relative path to the resource.
+
+    Since the test controller and the task machines run in different tmp dirs
+    on different machines the absolute path cannot be calculated correctly on
+    this machine. This function maps the relative path (from this directory)
+    to an absolute path on the task machine.
+    """
+    return self.task.rpc.AbsPath(path)
+
+  def CreateTask(self):
+    """Creates a task object and sets the proper values."""
+    self.task = self.CreateNewTask(
+        isolated_hash=self.args.task_machine,
+        dimensions={'os': 'Ubuntu-14.04', 'pool': 'Chromoting'})
+    self.task.Create()
+    self.task.WaitForConnection()
+
+  def ParseArgs(self):
+    """Gets the command line args."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--task_machine',
+                        help='isolated hash of the task machine.')
+    # The rest of the args are taken from
+    # testing/chromoting/browser_tests_launcher.py.
+    parser.add_argument('-f', '--commands_file',
+                        help='path to file listing commands to be launched.')
+    parser.add_argument('-p', '--prod_dir',
+                        help='path to folder having product and test binaries.')
+    parser.add_argument('-c', '--cfg_file',
+                        help='path to test host config file.')
+    parser.add_argument('--me2me_manifest_file',
+                        help='path to me2me host manifest file.')
+    parser.add_argument('--it2me_manifest_file',
+                        help='path to it2me host manifest file.')
+    parser.add_argument(
+        '-u', '--user_profile_dir',
+        help='path to user-profile-dir, used by connect-to-host tests.')
+    self.args, _ = parser.parse_known_args()
+
+  def TestIntegrationTests(self):
+    """Runs the integration tests via browser_tests_launcher.py."""
+    # Create a process object, configure it, and start it.
+    # All interactions with the process are based on this "proc" key.
+    proc = self.task.rpc.subprocess.Process(
+        self.CreateBrowserTestsLauncherCommand())
+    # Set the cwd to browser_tests_launcher relative to this directory.
+    # This allows browser_test_launcher to use relative paths.
+    self.task.rpc.subprocess.SetCwd(proc, '../')
+    # Set the task verbosity to true to allow stdout/stderr to be echo'ed to
+    # run_task's stdout/stderr on the task machine. This can assist in
+    # debugging.
+    self.task.rpc.subprocess.SetVerbose(proc)
+    # Set the process as detached to create it in a new process group.
+    self.task.rpc.subprocess.SetDetached(proc)
+    # Start the actual process on the task machine.
+    self.task.rpc.subprocess.Start(proc)
+
+    # Collect the stdout/stderr and emit it from this controller while the
+    # process is running.
+    while self.task.rpc.subprocess.Poll(proc) is None:
+      # Output the test's stdout and stderr in semi-realtime.
+      # This is not true realtime due to the RPC calls and the 1s sleep.
+      stdout, stderr = self.task.rpc.subprocess.ReadOutput(proc)
+      if stdout:
+        sys.stdout.write(stdout)
+      if stderr:
+        sys.stderr.write(stderr)
+      time.sleep(1)
+
+    # Get the return code, clean up the process object.
+    returncode = self.task.rpc.subprocess.GetReturncode(proc)
+    self.task.rpc.subprocess.Delete(proc)
+
+    # Pass or fail depending on the return code from the browser_tests_launcher.
+    if returncode != 0:
+      raise AssertionError('browser_tests_launcher failed with return code '
+                           '%i' % returncode)
+
+
+if __name__ == '__main__':
+  ExampleController().RunController()
diff --git a/testing/commit_queue/config.json b/testing/commit_queue/config.json
index 6ca3384..8c0a2e7 100644
--- a/testing/commit_queue/config.json
+++ b/testing/commit_queue/config.json
@@ -1,93 +1,185 @@
 {
-    "experimental_tryjobs": {
-        "10 percent experiment": {
-            "percentage": 0.10,
-            "trybots": {
-                "launched": {
-                    "tryserver.chromium.linux": {
-                        "android_amp_rel_tests_recipe": ["defaulttests"],
-                        "android_n5_rel_tests_recipe": ["defaulttests"],
-                        "cast_shell": ["defaulttests"],
-                        "cast_shell_apk": ["defaulttests"],
-                        "linux_arm_compile": ["defaulttests"],
-                        "linux_chromium_asan_rel_ng": ["defaulttests"],
-                        "linux_chromium_chromeos_ozone_rel_ng": ["defaulttests"]
-                    },
-                    "tryserver.chromium.mac": {
-                        "mac_chromium_gn_rel": ["defaulttests"]
-                    },
-                    "tryserver.chromium.win": {
-                        "win8_chromium_ng": ["defaulttests"],
-                        "win8_chromium_gn_rel": ["defaulttests"]
+    "commit_burst_delay": 60,
+    "commit_user": "commit-bot@chromium.org",
+    "committer_project": "chromium",
+    "cq_status_url": "https://chromium-cq-status.appspot.com",
+    "git_repo_url": "https://chromium.googlesource.com/chromium/src",
+    "hide_ref_in_committed_msg": true,
+    "max_commit_burst": 2,
+    "project_bases": [
+        ".*"
+    ],
+    "project_bases_legacy": [
+        "^svn\\:\\/\\/svn\\.chromium\\.org\\/chrome/trunk/src(|/.*)$",
+        "^svn\\:\\/\\/chrome\\-svn\\/chrome/trunk/src(|/.*)$",
+        "^svn\\:\\/\\/chrome\\-svn\\.corp\\/chrome/trunk/src(|/.*)$",
+        "^svn\\:\\/\\/chrome\\-svn\\.corp\\.google\\.com\\/chrome/trunk/src(|/.*)$",
+        "^http\\:\\/\\/src\\.chromium\\.org\\/svn/trunk/src(|/.*)$",
+        "^https\\:\\/\\/src\\.chromium\\.org\\/svn/trunk/src(|/.*)$",
+        "^http\\:\\/\\/src\\.chromium\\.org\\/chrome/trunk/src(|/.*)$",
+        "^https\\:\\/\\/src\\.chromium\\.org\\/chrome/trunk/src(|/.*)$"
+    ],
+    "project_bases_legacy_from_git_repo_url": true,
+    "remote_branch": "refs/pending/heads/master",
+    "rietveld_url": "https://codereview.chromium.org",
+    "rietveld_user": "commit-bot@chromium.org",
+    "skip_throttle_users": [
+        "commit-bot@chromium.org"
+    ],
+    "tree_status_url": "https://chromium-status.appspot.com",
+    "tryserver_url": "http://build.chromium.org/p/tryserver.chromium/",
+    "use_buildbucket_for_tryjobs": true,
+    "verifiers_no_patch": {
+        "experimental_try_job_verifier": {
+            "10 percent experiment": {
+                "percentage": 0.1,
+                "trybots": {
+                    "launched": {
+                        "tryserver.chromium.linux": {
+                            "android_amp_rel_tests_recipe": [
+                                "defaulttests"
+                            ],
+                            "cast_shell": [
+                                "defaulttests"
+                            ],
+                            "cast_shell_apk": [
+                                "defaulttests"
+                            ],
+                            "linux_arm_compile": [
+                                "defaulttests"
+                            ],
+                            "linux_chromium_asan_rel_ng": [
+                                "defaulttests"
+                            ]
+                        },
+                        "tryserver.chromium.mac": {
+                            "mac_chromium_gn_rel": [
+                                "defaulttests"
+                            ]
+                        },
+                        "tryserver.chromium.win": {
+                            "win8_chromium_gn_rel": [
+                                "defaulttests"
+                            ],
+                            "win8_chromium_ng": [
+                                "defaulttests"
+                            ]
+                        }
                     }
                 }
-            }
-        },
-        "50 percent experiment": {
-            "percentage": 0.50,
-            "trybots": {
-                "launched": {
-                    "tryserver.chromium.linux": {
-                    }
-                }
-            }
-        },
-        "75 percent experiment": {
-            "percentage": 0.75,
-            "trybots": {
-                "launched": {
-                    "tryserver.chromium.linux": {
-                    }
-                }
-            }
-        },
-        "100 percent experiment": {
-            "percentage": 1.00,
-            "trybots": {
-                "launched": {
-                    "tryserver.chromium.linux": {
-                    },
-                    "tryserver.chromium.mac": {
-                    }
-                }
-            }
-        }
-    },
-    "trybots": {
-        "launched": {
-          "tryserver.chromium.linux": {
-              "android_chromium_gn_compile_dbg": ["defaulttests"],
-              "android_chromium_gn_compile_rel": ["defaulttests"],
-              "android_compile_rel": ["defaulttests"],
-              "chromium_presubmit": ["defaulttests"],
-              "linux_android_rel_ng": ["defaulttests"],
-              "linux_chromium_asan_rel": ["defaulttests"],
-              "linux_chromium_chromeos_compile_dbg_ng": ["defaulttests"],
-              "linux_chromium_chromeos_rel_ng": ["defaulttests"],
-              "linux_chromium_clobber_rel_ng": ["defaulttests"],
-              "linux_chromium_gn_chromeos_rel": ["defaulttests"],
-              "linux_chromium_gn_dbg": ["defaulttests"],
-              "linux_chromium_gn_rel": ["defaulttests"],
-              "linux_chromium_rel_ng": ["defaulttests"],
-              "android_arm64_dbg_recipe": ["defaulttests"],
-              "android_clang_dbg_recipe": ["defaulttests"],
-              "android_aosp": ["compile"],
-              "linux_chromium_compile_dbg_32_ng": ["compile"]
             },
-            "tryserver.chromium.mac": {
-              "ios_dbg_simulator_ninja": ["defaulttests"],
-              "ios_rel_device_ninja": ["defaulttests"],
-              "mac_chromium_compile_dbg_ng": ["defaulttests"],
-              "mac_chromium_rel_ng": ["defaulttests"]
+            "100 percent experiment": {
+                "percentage": 1.0,
+                "trybots": {
+                    "launched": {
+                        "tryserver.chromium.linux": {},
+                        "tryserver.chromium.mac": {}
+                    }
+                }
             },
-            "tryserver.chromium.win": {
-              "win8_chromium_rel": ["defaulttests"],
-              "win_chromium_compile_dbg_ng": ["defaulttests"],
-              "win_chromium_rel_ng": ["defaulttests"],
-              "win_chromium_x64_rel_ng": ["defaulttests"]
+            "50 percent experiment": {
+                "percentage": 0.5,
+                "trybots": {
+                    "launched": {
+                        "tryserver.chromium.linux": {}
+                    }
+                }
+            },
+            "75 percent experiment": {
+                "percentage": 0.75,
+                "trybots": {
+                    "launched": {
+                        "tryserver.chromium.linux": {}
+                    }
+                }
             }
         },
-        "triggered": {
+        "reviewer_lgtm_verifier": {},
+        "tree_status_verifier": {},
+        "try_job_verifier": {
+            "launched": {
+                "tryserver.chromium.linux": {
+                    "android_arm64_dbg_recipe": [
+                        "defaulttests"
+                    ],
+                    "android_chromium_gn_compile_dbg": [
+                        "defaulttests"
+                    ],
+                    "android_chromium_gn_compile_rel": [
+                        "defaulttests"
+                    ],
+                    "android_clang_dbg_recipe": [
+                        "defaulttests"
+                    ],
+                    "android_compile_rel": [
+                        "defaulttests"
+                    ],
+                    "chromium_presubmit": [
+                        "defaulttests"
+                    ],
+                    "linux_android_rel_ng": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_asan_rel": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_chromeos_compile_dbg_ng": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_chromeos_ozone_rel_ng": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_chromeos_rel_ng": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_clobber_rel_ng": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_compile_dbg_32_ng": [
+                        "compile"
+                    ],
+                    "linux_chromium_gn_chromeos_rel": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_gn_dbg": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_gn_rel": [
+                        "defaulttests"
+                    ],
+                    "linux_chromium_rel_ng": [
+                        "defaulttests"
+                    ]
+                },
+                "tryserver.chromium.mac": {
+                    "ios_dbg_simulator_ninja": [
+                        "defaulttests"
+                    ],
+                    "ios_rel_device_ninja": [
+                        "defaulttests"
+                    ],
+                    "mac_chromium_compile_dbg_ng": [
+                        "defaulttests"
+                    ],
+                    "mac_chromium_rel_ng": [
+                        "defaulttests"
+                    ]
+                },
+                "tryserver.chromium.win": {
+                    "win8_chromium_rel": [
+                        "defaulttests"
+                    ],
+                    "win_chromium_compile_dbg_ng": [
+                        "defaulttests"
+                    ],
+                    "win_chromium_rel_ng": [
+                        "defaulttests"
+                    ],
+                    "win_chromium_x64_rel_ng": [
+                        "defaulttests"
+                    ]
+                }
+            }
         }
     }
 }
diff --git a/testing/iossim/iossim.gyp b/testing/iossim/iossim.gyp
index 6a1fb02..0fdd8b1 100644
--- a/testing/iossim/iossim.gyp
+++ b/testing/iossim/iossim.gyp
@@ -4,7 +4,7 @@
 
 {
   'conditions': [
-    ['OS!="ios" or "<(GENERATOR)"=="ninja"', {
+    ['OS!="ios" or "<(GENERATOR)"!="xcode" or "<(GENERATOR_FLAVOR)"=="ninja"', {
       'targets': [
         {
           'target_name': 'iossim',
@@ -126,7 +126,7 @@
           },
         },
       ],
-    }, {  # else, OS=="ios" and "<(GENERATOR)"!="ninja"
+    }, {  # else, OS=="ios" and "<(GENERATOR)"=="xcode" and "<(GENERATOR_FLAVOR)"!="ninja"
       'variables': {
         'ninja_output_dir': 'ninja-iossim',
         'ninja_product_dir':
diff --git a/third_party/boringssl/boringssl_unittest.cc b/third_party/boringssl/boringssl_unittest.cc
index 4cc0804..d568e33 100644
--- a/third_party/boringssl/boringssl_unittest.cc
+++ b/third_party/boringssl/boringssl_unittest.cc
@@ -116,6 +116,10 @@
      FILE_PATH_LITERAL("aes_256_cbc_sha1_ssl3_tests.txt")},
     {FILE_PATH_LITERAL("des-ede3-cbc-sha1-ssl3"),
      FILE_PATH_LITERAL("des_ede3_cbc_sha1_ssl3_tests.txt")},
+    {FILE_PATH_LITERAL("aes-128-ctr-hmac-sha256"),
+     FILE_PATH_LITERAL("aes_128_ctr_hmac_sha256.txt")},
+    {FILE_PATH_LITERAL("aes-256-ctr-hmac-sha256"),
+     FILE_PATH_LITERAL("aes_256_ctr_hmac_sha256.txt")},
 };
 
 TEST(BoringSSL, AEADs) {
diff --git a/third_party/boringssl/err_data.c b/third_party/boringssl/err_data.c
index 65bc34f..fe8e3d4 100644
--- a/third_party/boringssl/err_data.c
+++ b/third_party/boringssl/err_data.c
@@ -79,192 +79,194 @@
     0xc3d0679,
     0xc3d8681,
     0xc3e068c,
-    0x10321744,
-    0x1032975b,
-    0x10331774,
-    0x1033978a,
-    0x1034179a,
-    0x103497ad,
-    0x103517bb,
-    0x103597ca,
-    0x103617ea,
-    0x10369809,
-    0x10371826,
-    0x10379843,
-    0x10381858,
-    0x1038987a,
-    0x10391899,
-    0x103998b8,
-    0x103a18cf,
-    0x103a98e6,
-    0x103b18ef,
-    0x103b98fa,
-    0x103c1914,
-    0x103c991c,
-    0x103d1924,
-    0x103d992b,
-    0x103e193e,
-    0x103e9950,
-    0x103f1963,
-    0x103f996c,
-    0x143209cb,
-    0x143289d9,
-    0x143309e5,
-    0x143389f2,
-    0x1832100b,
-    0x18329023,
-    0x18331045,
-    0x18339057,
-    0x18341068,
-    0x18349081,
-    0x18351092,
-    0x183590a8,
-    0x183610b8,
-    0x183690cd,
-    0x183710e6,
-    0x183790f7,
-    0x1838110d,
-    0x1838911e,
-    0x18391130,
-    0x18399145,
-    0x183a1157,
-    0x183a9167,
-    0x183b117c,
-    0x183b9189,
-    0x183c119b,
-    0x183c91a9,
-    0x183d11bc,
-    0x183d91cc,
-    0x183e11e1,
-    0x183e91f2,
-    0x183f1205,
-    0x183f9214,
-    0x18401224,
-    0x18409231,
-    0x18411240,
-    0x18419251,
-    0x18421264,
-    0x18429276,
-    0x18431288,
-    0x18439299,
-    0x184412aa,
-    0x184492bb,
-    0x184512cc,
-    0x184592d9,
-    0x184612e7,
-    0x184692fa,
-    0x1847130e,
-    0x1847931b,
-    0x1848132a,
-    0x18489339,
-    0x1849134a,
-    0x18499357,
-    0x184a1365,
-    0x184a9376,
-    0x184b1387,
-    0x184b9395,
-    0x184c13a5,
-    0x184c93cb,
-    0x184d13da,
-    0x184d93ea,
-    0x184e13fa,
-    0x184e9409,
+    0x103217ce,
+    0x103297e5,
+    0x103317fe,
+    0x10339814,
+    0x10341824,
+    0x10349837,
+    0x10351845,
+    0x10359854,
+    0x10361874,
+    0x10369893,
+    0x103718b0,
+    0x103798cd,
+    0x103818e2,
+    0x10389904,
+    0x10391923,
+    0x10399942,
+    0x103a1959,
+    0x103a9970,
+    0x103b1979,
+    0x103b9984,
+    0x103c199e,
+    0x103c99a6,
+    0x103d19ae,
+    0x103d99b5,
+    0x103e19c8,
+    0x103e99da,
+    0x103f19ed,
+    0x103f99f6,
+    0x14320a25,
+    0x14328a33,
+    0x14330a3f,
+    0x14338a4c,
+    0x18321065,
+    0x1832907d,
+    0x1833109f,
+    0x183390b1,
+    0x183410e3,
+    0x183490fc,
+    0x1835110d,
+    0x18359123,
+    0x18361133,
+    0x18369148,
+    0x18371161,
+    0x18379172,
+    0x18381188,
+    0x18389199,
+    0x183911ab,
+    0x183991c0,
+    0x183a11d2,
+    0x183a91e2,
+    0x183b11f7,
+    0x183b9204,
+    0x183c1216,
+    0x183c9224,
+    0x183d1237,
+    0x183d9247,
+    0x183e125c,
+    0x183e926d,
+    0x183f1280,
+    0x183f928f,
+    0x1840129f,
+    0x184092ac,
+    0x184112bb,
+    0x184192cc,
+    0x184212df,
+    0x184292f1,
+    0x18431303,
+    0x18439314,
+    0x18441325,
+    0x18449336,
+    0x18451347,
+    0x18459354,
+    0x18461362,
+    0x18469375,
+    0x18471389,
+    0x18479396,
+    0x184813a5,
+    0x184893b4,
+    0x184913c5,
+    0x184993e1,
+    0x184a13ef,
+    0x184a9400,
+    0x184b1411,
+    0x184b941f,
+    0x184c142f,
+    0x184c9455,
+    0x184d1464,
+    0x184d9474,
+    0x184e1484,
+    0x184e9493,
+    0x184f13d2,
+    0x184f90c2,
     0x1c320699,
     0x1c3286a5,
     0x1c3306b0,
     0x1c3386bc,
-    0x2032141d,
-    0x20329428,
-    0x20331430,
-    0x2033943c,
-    0x24321448,
-    0x24329456,
-    0x24331468,
-    0x24339477,
-    0x2434148a,
-    0x2434949d,
-    0x243514b4,
-    0x243594cc,
-    0x243614da,
-    0x243694f2,
-    0x243714fb,
-    0x2437950d,
-    0x24381521,
-    0x2438952e,
-    0x24391544,
-    0x2439955c,
-    0x243a1574,
-    0x243a957e,
-    0x243b1593,
-    0x243b95a1,
-    0x243c15b9,
-    0x243c95d0,
-    0x243d15db,
-    0x243d95e9,
-    0x28320a2b,
-    0x28328a3a,
-    0x28330a45,
-    0x28338a4a,
-    0x28340a55,
-    0x2c322850,
-    0x2c32a85c,
-    0x2c33286f,
-    0x2c33a880,
-    0x2c342899,
-    0x2c34a8c1,
-    0x2c3528d8,
-    0x2c35a8f5,
-    0x2c362912,
-    0x2c36a92f,
-    0x2c372948,
-    0x2c37a961,
-    0x2c382977,
-    0x2c38a985,
-    0x2c392997,
-    0x2c39a9b4,
-    0x2c3a29d1,
-    0x2c3aa9df,
-    0x2c3b29fd,
-    0x2c3baa1b,
-    0x2c3c2a36,
-    0x2c3caa4a,
-    0x2c3d2a5c,
-    0x2c3daa6c,
-    0x2c3e2a7a,
-    0x2c3eaa8a,
-    0x2c3f2a9a,
-    0x2c3faab5,
-    0x2c402ac6,
-    0x2c40aae1,
-    0x2c412af5,
-    0x2c41ab08,
-    0x2c422b27,
-    0x2c42ab3b,
-    0x2c432b4e,
-    0x2c43ab5d,
-    0x2c442b6c,
-    0x2c44ab83,
-    0x2c452b9e,
-    0x2c45abb6,
-    0x2c462bca,
-    0x2c46abdd,
-    0x2c472bee,
-    0x2c47abff,
-    0x2c482c10,
-    0x2c48ac21,
-    0x2c492c30,
-    0x2c49ac3d,
-    0x2c4a2c4a,
-    0x2c4aac57,
-    0x2c4b2c60,
-    0x2c4bac74,
-    0x2c4c2c83,
-    0x2c4cac91,
-    0x2c4d2cb3,
-    0x2c4dacc4,
-    0x2c4e2cd5,
-    0x2c4eaca0,
-    0x2c4f28b2,
+    0x203214a7,
+    0x203294b2,
+    0x203314ba,
+    0x203394c6,
+    0x243214d2,
+    0x243294e0,
+    0x243314f2,
+    0x24339501,
+    0x24341514,
+    0x24349527,
+    0x2435153e,
+    0x24359556,
+    0x24361564,
+    0x2436957c,
+    0x24371585,
+    0x24379597,
+    0x243815ab,
+    0x243895b8,
+    0x243915ce,
+    0x243995e6,
+    0x243a15fe,
+    0x243a9608,
+    0x243b161d,
+    0x243b962b,
+    0x243c1643,
+    0x243c965a,
+    0x243d1665,
+    0x243d9673,
+    0x28320a85,
+    0x28328a94,
+    0x28330a9f,
+    0x28338aa4,
+    0x28340aaf,
+    0x2c3228da,
+    0x2c32a8e6,
+    0x2c3328f9,
+    0x2c33a90a,
+    0x2c342923,
+    0x2c34a94b,
+    0x2c352962,
+    0x2c35a97f,
+    0x2c36299c,
+    0x2c36a9b9,
+    0x2c3729d2,
+    0x2c37a9eb,
+    0x2c382a01,
+    0x2c38aa0f,
+    0x2c392a21,
+    0x2c39aa3e,
+    0x2c3a2a5b,
+    0x2c3aaa69,
+    0x2c3b2a87,
+    0x2c3baaa5,
+    0x2c3c2ac0,
+    0x2c3caad4,
+    0x2c3d2ae6,
+    0x2c3daaf6,
+    0x2c3e2b04,
+    0x2c3eab14,
+    0x2c3f2b24,
+    0x2c3fab3f,
+    0x2c402b50,
+    0x2c40ab6b,
+    0x2c412b7f,
+    0x2c41ab92,
+    0x2c422bb1,
+    0x2c42abc5,
+    0x2c432bd8,
+    0x2c43abe7,
+    0x2c442bf6,
+    0x2c44ac0d,
+    0x2c452c28,
+    0x2c45ac40,
+    0x2c462c54,
+    0x2c46ac67,
+    0x2c472c78,
+    0x2c47ac89,
+    0x2c482c9a,
+    0x2c48acab,
+    0x2c492cba,
+    0x2c49acc7,
+    0x2c4a2cd4,
+    0x2c4aace1,
+    0x2c4b2cea,
+    0x2c4bacfe,
+    0x2c4c2d0d,
+    0x2c4cad1b,
+    0x2c4d2d3d,
+    0x2c4dad4e,
+    0x2c4e2d5f,
+    0x2c4ead2a,
+    0x2c4f293c,
     0x30320000,
     0x30328018,
     0x3033002c,
@@ -329,239 +331,239 @@
     0x30508404,
     0x30510413,
     0x3051841c,
-    0x3432095d,
-    0x3432896d,
-    0x34330978,
-    0x34338985,
-    0x3832098e,
-    0x383289a1,
-    0x383309ab,
-    0x383389bd,
-    0x3c320a5c,
-    0x3c328a6a,
-    0x3c330a81,
-    0x3c338a95,
-    0x3c340ab0,
-    0x3c348ac1,
-    0x3c350acd,
-    0x3c358ae1,
-    0x3c360af3,
-    0x3c368b1c,
-    0x3c370b29,
-    0x3c378b36,
-    0x3c380b44,
-    0x3c388b51,
-    0x3c390b5e,
-    0x3c398b82,
-    0x3c3a0b92,
-    0x3c3a8baa,
-    0x3c3b0bbf,
-    0x3c3b8bd4,
-    0x3c3c0be1,
-    0x3c3c8bf4,
-    0x3c3d0c07,
-    0x3c3d8c2b,
-    0x3c3e0c53,
-    0x3c3e8c6c,
-    0x3c3f0c82,
-    0x3c3f8c8f,
-    0x3c400ca2,
-    0x3c408cb3,
-    0x3c410cc4,
-    0x3c418cdd,
-    0x3c420cf6,
-    0x3c428d0c,
-    0x3c430d29,
-    0x3c438d3f,
-    0x3c440d5b,
-    0x3c448d82,
-    0x3c450da0,
-    0x3c458dba,
-    0x3c460dd2,
-    0x3c468dea,
-    0x3c470e15,
-    0x3c478e40,
-    0x3c480e61,
-    0x3c488e8a,
-    0x3c490ea5,
-    0x3c498ec0,
-    0x3c4a0ecd,
-    0x3c4a8ee4,
-    0x3c4b0efb,
-    0x3c4b8f24,
-    0x3c4c0f34,
-    0x3c4c8f40,
-    0x3c4d0f58,
-    0x3c4d8f6b,
-    0x3c4e0f7c,
-    0x3c4e8f8d,
-    0x3c4f0f9d,
-    0x40321977,
-    0x40329991,
-    0x4033199d,
-    0x403399b5,
-    0x403419d3,
-    0x403499f2,
-    0x40351a09,
-    0x40359a25,
-    0x40361a41,
-    0x40369a5b,
-    0x40371a7a,
-    0x40379a99,
-    0x40381ab1,
-    0x40389ace,
-    0x40391af1,
-    0x40399b0e,
-    0x403a1b2c,
-    0x403a9b3c,
-    0x403b1b51,
-    0x403b9b6d,
-    0x403c1b87,
-    0x403c9b92,
-    0x403d1bb5,
-    0x403d9bd9,
-    0x403e1bef,
-    0x403e9bf9,
-    0x403f1c05,
-    0x403f9c16,
-    0x40401c2e,
-    0x40409c36,
-    0x40411c3f,
-    0x40419c48,
-    0x40421c58,
-    0x40429c6c,
-    0x40431c77,
-    0x40439c83,
-    0x40441c9e,
-    0x40449caa,
-    0x40451cb7,
-    0x40459cca,
-    0x40461ce2,
-    0x40469cfa,
-    0x40471d10,
-    0x40479d2b,
-    0x40481d46,
-    0x40489d5a,
-    0x40491d73,
-    0x40499d8c,
-    0x404a1da6,
-    0x404a9db0,
-    0x404b1dc0,
-    0x404b9de1,
-    0x404c1dfc,
-    0x404c9e0a,
-    0x404d1e17,
-    0x404d9e2b,
-    0x404e1e43,
-    0x404e9e51,
-    0x404f1e7b,
-    0x404f9e92,
-    0x40501ea4,
-    0x40509ed5,
-    0x40511f06,
-    0x40519f1b,
-    0x40521f2c,
-    0x40529f4c,
-    0x40531f67,
-    0x40539f77,
-    0x40541f83,
-    0x40549f96,
-    0x40551fac,
-    0x40559fca,
-    0x40561fd7,
-    0x40569fe1,
-    0x40571fef,
-    0x4057a00a,
-    0x40582025,
-    0x4058a044,
-    0x40592059,
-    0x4059a06e,
-    0x405a208b,
-    0x405aa09f,
-    0x405b20bb,
-    0x405ba0d1,
-    0x405c20ee,
-    0x405ca100,
-    0x405d2117,
-    0x405da128,
-    0x405e2144,
-    0x405ea158,
-    0x405f2168,
-    0x405fa184,
-    0x40602199,
-    0x4060a1af,
-    0x406121cc,
-    0x4061a1e5,
-    0x406221f8,
-    0x4062a201,
-    0x40632211,
-    0x4063a21d,
-    0x40642233,
-    0x4064a251,
-    0x40652266,
-    0x4065a283,
-    0x4066229a,
-    0x4066a2b8,
-    0x406722d5,
-    0x4067a2ec,
-    0x4068230a,
-    0x4068a321,
-    0x40692339,
-    0x4069a34a,
-    0x406a235d,
-    0x406aa370,
-    0x406b2384,
-    0x406ba3a8,
-    0x406c23c3,
-    0x406ca3e4,
-    0x406d2408,
-    0x406da423,
-    0x406e2444,
-    0x406ea459,
-    0x406f2472,
-    0x406fa47f,
-    0x4070248d,
-    0x4070a49a,
-    0x407124b7,
-    0x4071a4d7,
-    0x407224f2,
-    0x4072a50b,
-    0x40732522,
-    0x4073a53c,
-    0x40742560,
-    0x4074a576,
-    0x4075258a,
-    0x4075a59f,
-    0x407625b9,
-    0x4076a5cb,
-    0x407725e0,
-    0x4077a606,
-    0x40782623,
-    0x4078a646,
-    0x4079266c,
-    0x4079a689,
-    0x407a26ac,
-    0x407aa6c8,
-    0x407b26e4,
-    0x407ba6f6,
-    0x407c2703,
-    0x407ca710,
-    0x407d272d,
-    0x407da744,
-    0x407e2760,
-    0x407ea776,
-    0x407f278e,
-    0x407fa7a1,
-    0x408027b6,
-    0x4080a7cf,
-    0x408127ed,
-    0x4081a80d,
-    0x40822816,
-    0x4082a832,
-    0x4083283b,
-    0x40839e60,
-    0x40841eef,
-    0x40849ebf,
+    0x343209b7,
+    0x343289c7,
+    0x343309d2,
+    0x343389df,
+    0x383209e8,
+    0x383289fb,
+    0x38330a05,
+    0x38338a17,
+    0x3c320ab6,
+    0x3c328ac4,
+    0x3c330adb,
+    0x3c338aef,
+    0x3c340b0a,
+    0x3c348b1b,
+    0x3c350b27,
+    0x3c358b3b,
+    0x3c360b4d,
+    0x3c368b76,
+    0x3c370b83,
+    0x3c378b90,
+    0x3c380b9e,
+    0x3c388bab,
+    0x3c390bb8,
+    0x3c398bdc,
+    0x3c3a0bec,
+    0x3c3a8c04,
+    0x3c3b0c19,
+    0x3c3b8c2e,
+    0x3c3c0c3b,
+    0x3c3c8c4e,
+    0x3c3d0c61,
+    0x3c3d8c85,
+    0x3c3e0cad,
+    0x3c3e8cc6,
+    0x3c3f0cdc,
+    0x3c3f8ce9,
+    0x3c400cfc,
+    0x3c408d0d,
+    0x3c410d1e,
+    0x3c418d37,
+    0x3c420d50,
+    0x3c428d66,
+    0x3c430d83,
+    0x3c438d99,
+    0x3c440db5,
+    0x3c448ddc,
+    0x3c450dfa,
+    0x3c458e14,
+    0x3c460e2c,
+    0x3c468e44,
+    0x3c470e6f,
+    0x3c478e9a,
+    0x3c480ebb,
+    0x3c488ee4,
+    0x3c490eff,
+    0x3c498f1a,
+    0x3c4a0f27,
+    0x3c4a8f3e,
+    0x3c4b0f55,
+    0x3c4b8f7e,
+    0x3c4c0f8e,
+    0x3c4c8f9a,
+    0x3c4d0fb2,
+    0x3c4d8fc5,
+    0x3c4e0fd6,
+    0x3c4e8fe7,
+    0x3c4f0ff7,
+    0x40321a01,
+    0x40329a1b,
+    0x40331a27,
+    0x40339a3f,
+    0x40341a5d,
+    0x40349a7c,
+    0x40351a93,
+    0x40359aaf,
+    0x40361acb,
+    0x40369ae5,
+    0x40371b04,
+    0x40379b23,
+    0x40381b3b,
+    0x40389b58,
+    0x40391b7b,
+    0x40399b98,
+    0x403a1bb6,
+    0x403a9bc6,
+    0x403b1bdb,
+    0x403b9bf7,
+    0x403c1c11,
+    0x403c9c1c,
+    0x403d1c3f,
+    0x403d9c63,
+    0x403e1c79,
+    0x403e9c83,
+    0x403f1c8f,
+    0x403f9ca0,
+    0x40401cb8,
+    0x40409cc0,
+    0x40411cc9,
+    0x40419cd2,
+    0x40421ce2,
+    0x40429cf6,
+    0x40431d01,
+    0x40439d0d,
+    0x40441d28,
+    0x40449d34,
+    0x40451d41,
+    0x40459d54,
+    0x40461d6c,
+    0x40469d84,
+    0x40471d9a,
+    0x40479db5,
+    0x40481dd0,
+    0x40489de4,
+    0x40491dfd,
+    0x40499e16,
+    0x404a1e30,
+    0x404a9e3a,
+    0x404b1e4a,
+    0x404b9e6b,
+    0x404c1e86,
+    0x404c9e94,
+    0x404d1ea1,
+    0x404d9eb5,
+    0x404e1ecd,
+    0x404e9edb,
+    0x404f1f05,
+    0x404f9f1c,
+    0x40501f2e,
+    0x40509f5f,
+    0x40511f90,
+    0x40519fa5,
+    0x40521fb6,
+    0x40529fd6,
+    0x40531ff1,
+    0x4053a001,
+    0x4054200d,
+    0x4054a020,
+    0x40552036,
+    0x4055a054,
+    0x40562061,
+    0x4056a06b,
+    0x40572079,
+    0x4057a094,
+    0x405820af,
+    0x4058a0ce,
+    0x405920e3,
+    0x4059a0f8,
+    0x405a2115,
+    0x405aa129,
+    0x405b2145,
+    0x405ba15b,
+    0x405c2178,
+    0x405ca18a,
+    0x405d21a1,
+    0x405da1b2,
+    0x405e21ce,
+    0x405ea1e2,
+    0x405f21f2,
+    0x405fa20e,
+    0x40602223,
+    0x4060a239,
+    0x40612256,
+    0x4061a26f,
+    0x40622282,
+    0x4062a28b,
+    0x4063229b,
+    0x4063a2a7,
+    0x406422bd,
+    0x4064a2db,
+    0x406522f0,
+    0x4065a30d,
+    0x40662324,
+    0x4066a342,
+    0x4067235f,
+    0x4067a376,
+    0x40682394,
+    0x4068a3ab,
+    0x406923c3,
+    0x4069a3d4,
+    0x406a23e7,
+    0x406aa3fa,
+    0x406b240e,
+    0x406ba432,
+    0x406c244d,
+    0x406ca46e,
+    0x406d2492,
+    0x406da4ad,
+    0x406e24ce,
+    0x406ea4e3,
+    0x406f24fc,
+    0x406fa509,
+    0x40702517,
+    0x4070a524,
+    0x40712541,
+    0x4071a561,
+    0x4072257c,
+    0x4072a595,
+    0x407325ac,
+    0x4073a5c6,
+    0x407425ea,
+    0x4074a600,
+    0x40752614,
+    0x4075a629,
+    0x40762643,
+    0x4076a655,
+    0x4077266a,
+    0x4077a690,
+    0x407826ad,
+    0x4078a6d0,
+    0x407926f6,
+    0x4079a713,
+    0x407a2736,
+    0x407aa752,
+    0x407b276e,
+    0x407ba780,
+    0x407c278d,
+    0x407ca79a,
+    0x407d27b7,
+    0x407da7ce,
+    0x407e27ea,
+    0x407ea800,
+    0x407f2818,
+    0x407fa82b,
+    0x40802840,
+    0x4080a859,
+    0x40812877,
+    0x4081a897,
+    0x408228a0,
+    0x4082a8bc,
+    0x408328c5,
+    0x40839eea,
+    0x40841f79,
+    0x40849f49,
     0x4432042a,
     0x4432843c,
     0x44330445,
@@ -580,89 +582,89 @@
     0x44398522,
     0x443a052c,
     0x443a8536,
-    0x4c3215f1,
-    0x4c329600,
-    0x4c33160f,
-    0x4c339628,
-    0x4c341643,
-    0x4c34965f,
-    0x4c351671,
-    0x4c35967f,
-    0x4c361694,
-    0x4c3696a5,
-    0x4c3716b3,
-    0x4c3796c1,
-    0x4c3816d3,
-    0x4c3896e3,
-    0x4c3916ed,
-    0x4c399705,
-    0x4c3a171d,
-    0x4c3a9730,
-    0x50322ce6,
-    0x5032acfb,
-    0x50332d0c,
-    0x5033ad1f,
-    0x50342d30,
-    0x5034ad43,
-    0x50352d52,
-    0x5035ad67,
-    0x50362d77,
-    0x5036ad86,
-    0x50372d97,
-    0x5037ada7,
-    0x50382db8,
-    0x5038adcb,
-    0x50392ddd,
-    0x5039adf3,
-    0x503a2e05,
-    0x503aae16,
-    0x503b2e27,
-    0x503bae38,
-    0x503c2e43,
-    0x503cae4f,
-    0x503d2e5a,
-    0x503dae65,
-    0x503e2e72,
-    0x503eae87,
-    0x503f2e95,
-    0x503faea9,
-    0x50402ebc,
-    0x5040aecd,
-    0x50412ee7,
-    0x5041aef6,
-    0x50422eff,
-    0x5042af0e,
-    0x50432f20,
-    0x5043af2c,
-    0x50442f34,
-    0x5044af47,
-    0x50452f58,
-    0x5045af6e,
-    0x50462f7a,
-    0x5046af8e,
-    0x50472f9c,
-    0x5047afb0,
-    0x50482fca,
-    0x5048afde,
-    0x50492ff4,
-    0x5049b00b,
-    0x504a301d,
-    0x504ab031,
-    0x504b3046,
-    0x504bb05d,
-    0x504c3071,
-    0x504cb07a,
-    0x504d3082,
-    0x504db091,
-    0x504e30a1,
-    0x68320fbe,
-    0x68328fcf,
-    0x68330fdf,
-    0x68338fed,
-    0x68340ffa,
-    0x6c320fad,
-    0x74320a06,
-    0x74328a18,
+    0x4c32167b,
+    0x4c32968a,
+    0x4c331699,
+    0x4c3396b2,
+    0x4c3416cd,
+    0x4c3496e9,
+    0x4c3516fb,
+    0x4c359709,
+    0x4c36171e,
+    0x4c36972f,
+    0x4c37173d,
+    0x4c37974b,
+    0x4c38175d,
+    0x4c38976d,
+    0x4c391777,
+    0x4c39978f,
+    0x4c3a17a7,
+    0x4c3a97ba,
+    0x50322d70,
+    0x5032ad85,
+    0x50332d96,
+    0x5033ada9,
+    0x50342dba,
+    0x5034adcd,
+    0x50352ddc,
+    0x5035adf1,
+    0x50362e01,
+    0x5036ae10,
+    0x50372e21,
+    0x5037ae31,
+    0x50382e42,
+    0x5038ae55,
+    0x50392e67,
+    0x5039ae7d,
+    0x503a2e8f,
+    0x503aaea0,
+    0x503b2eb1,
+    0x503baec2,
+    0x503c2ecd,
+    0x503caed9,
+    0x503d2ee4,
+    0x503daeef,
+    0x503e2efc,
+    0x503eaf11,
+    0x503f2f1f,
+    0x503faf33,
+    0x50402f46,
+    0x5040af57,
+    0x50412f71,
+    0x5041af80,
+    0x50422f89,
+    0x5042af98,
+    0x50432faa,
+    0x5043afb6,
+    0x50442fbe,
+    0x5044afd1,
+    0x50452fe2,
+    0x5045aff8,
+    0x50463004,
+    0x5046b018,
+    0x50473026,
+    0x5047b03a,
+    0x50483054,
+    0x5048b068,
+    0x5049307e,
+    0x5049b095,
+    0x504a30a7,
+    0x504ab0bb,
+    0x504b30d0,
+    0x504bb0e7,
+    0x504c30fb,
+    0x504cb104,
+    0x504d310c,
+    0x504db11b,
+    0x504e312b,
+    0x68321018,
+    0x68329029,
+    0x68331039,
+    0x68339047,
+    0x68341054,
+    0x6c321007,
+    0x74320a60,
+    0x74328a72,
     0x783206c9,
     0x783286fc,
     0x7833070e,
@@ -672,30 +674,33 @@
     0x78350766,
     0x78358778,
     0x7836078c,
-    0x783687a0,
-    0x783707b2,
-    0x783787c4,
-    0x783807d6,
-    0x783887ed,
-    0x78390804,
-    0x7839881b,
-    0x783a0837,
-    0x783a8853,
-    0x783b086f,
-    0x783b8885,
-    0x783c089b,
-    0x783c88b1,
-    0x783d08ce,
-    0x783d88dd,
-    0x783e08ec,
-    0x783e88fb,
-    0x783f0917,
-    0x783f8925,
-    0x78400933,
-    0x78408941,
-    0x7841094e,
+    0x783687fa,
+    0x7837080c,
+    0x7837881e,
+    0x78380830,
+    0x78388847,
+    0x7839085e,
+    0x78398875,
+    0x783a0891,
+    0x783a88ad,
+    0x783b08c9,
+    0x783b88df,
+    0x783c08f5,
+    0x783c890b,
+    0x783d0928,
+    0x783d8937,
+    0x783e0946,
+    0x783e8955,
+    0x783f0971,
+    0x783f897f,
+    0x7840098d,
+    0x7840899b,
+    0x784109a8,
     0x784186db,
-    0x80321418,
+    0x784207a0,
+    0x784287be,
+    0x784307dc,
+    0x803214a2,
 };
 
 const size_t kOpenSSLFunctionValuesLen = sizeof(kOpenSSLFunctionValues) / sizeof(kOpenSSLFunctionValues[0]);
@@ -822,6 +827,9 @@
     "EVP_CipherInit_ex\0"
     "EVP_DecryptFinal_ex\0"
     "EVP_EncryptFinal_ex\0"
+    "aead_aes_ctr_hmac_sha256_init\0"
+    "aead_aes_ctr_hmac_sha256_open\0"
+    "aead_aes_ctr_hmac_sha256_seal\0"
     "aead_aes_gcm_init\0"
     "aead_aes_gcm_open\0"
     "aead_aes_gcm_seal\0"
@@ -932,6 +940,7 @@
     "EVP_DigestVerifyInitFromAlgorithm\0"
     "EVP_PKEY_CTX_ctrl\0"
     "EVP_PKEY_CTX_dup\0"
+    "EVP_PKEY_CTX_get0_rsa_oaep_label\0"
     "EVP_PKEY_copy_parameters\0"
     "EVP_PKEY_decrypt\0"
     "EVP_PKEY_decrypt_init\0"
@@ -975,6 +984,7 @@
     "pkey_ec_keygen\0"
     "pkey_ec_paramgen\0"
     "pkey_ec_sign\0"
+    "pkey_hmac_ctrl\0"
     "pkey_rsa_ctrl\0"
     "pkey_rsa_decrypt\0"
     "pkey_rsa_encrypt\0"
diff --git a/third_party/harfbuzz-ng/NEWS b/third_party/harfbuzz-ng/NEWS
index 3ff8e4c..c4950e2 100644
--- a/third_party/harfbuzz-ng/NEWS
+++ b/third_party/harfbuzz-ng/NEWS
@@ -1,3 +1,64 @@
+Overview of changes leading to 0.9.40
+Friday, March 20, 2015
+=====================================
+
+- Another hb-coretext crasher fix.  Ouch!
+- Happy Norouz!
+
+
+Overview of changes leading to 0.9.39
+Wednesday, March 4, 2015
+=====================================
+
+- Critical hb-coretext fixes.
+- Optimizations and refactoring; no functional change
+  expected.
+- Misc build fixes.
+
+
+Overview of changes leading to 0.9.38
+Friday, January 23, 2015
+=====================================
+
+- Fix minor out-of-bounds access in Indic shaper.
+- Change New Tai Lue shaping engine from South-East Asian to default,
+  reflecting change in Unicode encoding model.
+- Add hb-shape --font-size.  Can take up to two numbers for separate
+  x / y size.
+- Fix CoreText and FreeType scale issues with negative scales.
+- Reject blobs larger than 2GB.  This might break some icu-le-hb clients
+  that need security fixes.  See:
+  http://www.icu-project.org/trac/ticket/11450
+- Avoid accessing font tables during face destruction, in casce rogue
+  clients released face data already.
+- Fix up gobject-introspection a bit.  Python bindings kinda working.
+  See README.python.
+- Misc fixes.
+- API additions:
+  hb_ft_face_create_referenced()
+  hb_ft_font_create_referenced()
+
+
+Overview of changes leading to 0.9.37
+Wednesday, December 17, 2014
+=====================================
+
+- Fix out-of-bounds access in Context lookup format 3.
+- Indic: Allow ZWJ/ZWNJ before syllable modifiers.
+
+
+Overview of changes leading to 0.9.36
+Thursday, November 20, 2014
+=====================================
+
+- First time that three months went by without a release since
+  0.9.2 was released on August 10, 2012!
+- Fix performance bug in hb_ot_collect_glyphs():
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1090869
+- Add basic vertical-text support to hb-ot-font.
+- Misc build fixes.
+
+
 Overview of changes leading to 0.9.35
 Saturday, August 13, 2014
 =====================================
@@ -80,6 +141,16 @@
     EOT flag.
 
 
+Overview of changes leading to 0.9.33
+Tuesday, July 22, 2014
+=====================================
+
+- Turn off ARabic 'cswh' feature that was accidentally turned on.
+- Add HB_TAG_MAX_SIGNED.
+- Make hb_face_make_immutable() really make face immutable!
+- Windows build fixes.
+
+
 Overview of changes leading to 0.9.32
 Thursday, July 17, 2014
 =====================================
diff --git a/third_party/harfbuzz-ng/README b/third_party/harfbuzz-ng/README
index 74e739d..d34bc74 100644
--- a/third_party/harfbuzz-ng/README
+++ b/third_party/harfbuzz-ng/README
@@ -1,3 +1,6 @@
+[![Build Status](https://travis-ci.org/behdad/harfbuzz.svg)](https://travis-ci.org/behdad/harfbuzz)
+[![Coverage Status](https://img.shields.io/coveralls/behdad/harfbuzz.svg)](https://coveralls.io/r/behdad/harfbuzz)
+
 This is HarfBuzz, a text shaping library.
 
 For bug reports, mailing list, and other information please visit:
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index 5c70ed1..d38a5d0 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,8 +1,8 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 0.9.38
-Date: 20150212
+Version: 0.9.40
+Date: 20150320
 Security Critical: yes
 License: MIT
 License File: COPYING
diff --git a/third_party/harfbuzz-ng/src/hb-buffer-deserialize-text.hh b/third_party/harfbuzz-ng/src/hb-buffer-deserialize-text.hh
index 803efbd..7a46ab2 100644
--- a/third_party/harfbuzz-ng/src/hb-buffer-deserialize-text.hh
+++ b/third_party/harfbuzz-ng/src/hb-buffer-deserialize-text.hh
@@ -1,5 +1,5 @@
 
-#line 1 "../../src/hb-buffer-deserialize-text.rl"
+#line 1 "hb-buffer-deserialize-text.rl"
 /*
  * Copyright © 2013  Google, Inc.
  *
@@ -32,7 +32,7 @@
 #include "hb-private.hh"
 
 
-#line 36 "../../src/hb-buffer-deserialize-text.hh"
+#line 36 "hb-buffer-deserialize-text.hh"
 static const unsigned char _deserialize_text_trans_keys[] = {
 	0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 
 	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 
@@ -312,7 +312,7 @@
 static const int deserialize_text_en_main = 1;
 
 
-#line 91 "../../src/hb-buffer-deserialize-text.rl"
+#line 91 "hb-buffer-deserialize-text.rl"
 
 
 static hb_bool_t
@@ -339,12 +339,12 @@
   hb_glyph_info_t info;
   hb_glyph_position_t pos;
   
-#line 343 "../../src/hb-buffer-deserialize-text.hh"
+#line 343 "hb-buffer-deserialize-text.hh"
 	{
 	cs = deserialize_text_start;
 	}
 
-#line 348 "../../src/hb-buffer-deserialize-text.hh"
+#line 348 "hb-buffer-deserialize-text.hh"
 	{
 	int _slen;
 	int _trans;
@@ -370,13 +370,13 @@
 
 	switch ( _deserialize_text_trans_actions[_trans] ) {
 	case 2:
-#line 51 "../../src/hb-buffer-deserialize-text.rl"
+#line 51 "hb-buffer-deserialize-text.rl"
 	{
 	tok = p;
 }
 	break;
 	case 5:
-#line 55 "../../src/hb-buffer-deserialize-text.rl"
+#line 55 "hb-buffer-deserialize-text.rl"
 	{
 	if (!hb_font_glyph_from_string (font,
 					tok, p - tok,
@@ -385,41 +385,41 @@
 }
 	break;
 	case 10:
-#line 62 "../../src/hb-buffer-deserialize-text.rl"
+#line 62 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
 	break;
 	case 3:
-#line 63 "../../src/hb-buffer-deserialize-text.rl"
+#line 63 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
 	break;
 	case 12:
-#line 64 "../../src/hb-buffer-deserialize-text.rl"
+#line 64 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
 	break;
 	case 7:
-#line 65 "../../src/hb-buffer-deserialize-text.rl"
+#line 65 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
 	break;
 	case 1:
-#line 38 "../../src/hb-buffer-deserialize-text.rl"
+#line 38 "hb-buffer-deserialize-text.rl"
 	{
 	memset (&info, 0, sizeof (info));
 	memset (&pos , 0, sizeof (pos ));
 }
-#line 51 "../../src/hb-buffer-deserialize-text.rl"
+#line 51 "hb-buffer-deserialize-text.rl"
 	{
 	tok = p;
 }
 	break;
 	case 4:
-#line 55 "../../src/hb-buffer-deserialize-text.rl"
+#line 55 "hb-buffer-deserialize-text.rl"
 	{
 	if (!hb_font_glyph_from_string (font,
 					tok, p - tok,
 					&info.codepoint))
 	  return false;
 }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -429,9 +429,9 @@
 }
 	break;
 	case 9:
-#line 62 "../../src/hb-buffer-deserialize-text.rl"
+#line 62 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -441,9 +441,9 @@
 }
 	break;
 	case 11:
-#line 64 "../../src/hb-buffer-deserialize-text.rl"
+#line 64 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -453,9 +453,9 @@
 }
 	break;
 	case 6:
-#line 65 "../../src/hb-buffer-deserialize-text.rl"
+#line 65 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -465,9 +465,9 @@
 }
 	break;
 	case 8:
-#line 66 "../../src/hb-buffer-deserialize-text.rl"
+#line 66 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -476,7 +476,7 @@
 	*end_ptr = p;
 }
 	break;
-#line 480 "../../src/hb-buffer-deserialize-text.hh"
+#line 480 "hb-buffer-deserialize-text.hh"
 	}
 
 _again:
@@ -489,14 +489,14 @@
 	{
 	switch ( _deserialize_text_eof_actions[cs] ) {
 	case 4:
-#line 55 "../../src/hb-buffer-deserialize-text.rl"
+#line 55 "hb-buffer-deserialize-text.rl"
 	{
 	if (!hb_font_glyph_from_string (font,
 					tok, p - tok,
 					&info.codepoint))
 	  return false;
 }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -506,9 +506,9 @@
 }
 	break;
 	case 9:
-#line 62 "../../src/hb-buffer-deserialize-text.rl"
+#line 62 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -518,9 +518,9 @@
 }
 	break;
 	case 11:
-#line 64 "../../src/hb-buffer-deserialize-text.rl"
+#line 64 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -530,9 +530,9 @@
 }
 	break;
 	case 6:
-#line 65 "../../src/hb-buffer-deserialize-text.rl"
+#line 65 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -542,9 +542,9 @@
 }
 	break;
 	case 8:
-#line 66 "../../src/hb-buffer-deserialize-text.rl"
+#line 66 "hb-buffer-deserialize-text.rl"
 	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
-#line 43 "../../src/hb-buffer-deserialize-text.rl"
+#line 43 "hb-buffer-deserialize-text.rl"
 	{
 	buffer->add_info (info);
 	if (buffer->in_error)
@@ -553,14 +553,14 @@
 	*end_ptr = p;
 }
 	break;
-#line 557 "../../src/hb-buffer-deserialize-text.hh"
+#line 557 "hb-buffer-deserialize-text.hh"
 	}
 	}
 
 	_out: {}
 	}
 
-#line 119 "../../src/hb-buffer-deserialize-text.rl"
+#line 119 "hb-buffer-deserialize-text.rl"
 
 
   *end_ptr = p;
diff --git a/third_party/harfbuzz-ng/src/hb-buffer.cc b/third_party/harfbuzz-ng/src/hb-buffer.cc
index 942177c..b9fe263 100644
--- a/third_party/harfbuzz-ng/src/hb-buffer.cc
+++ b/third_party/harfbuzz-ng/src/hb-buffer.cc
@@ -443,7 +443,7 @@
 {
   unsigned int i, j;
 
-  if (start == end - 1)
+  if (end - start < 2)
     return;
 
   for (i = start, j = end - 1; i < j; i++, j--) {
diff --git a/third_party/harfbuzz-ng/src/hb-ft.cc b/third_party/harfbuzz-ng/src/hb-ft.cc
index f57f566..322f93a 100644
--- a/third_party/harfbuzz-ng/src/hb-ft.cc
+++ b/third_party/harfbuzz-ng/src/hb-ft.cc
@@ -118,6 +118,9 @@
   if (unlikely (FT_Get_Advance (ft_face, glyph, load_flags, &v)))
     return 0;
 
+  if (font->y_scale < 0)
+    v = -v;
+
   /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
    * have a Y growing upward.  Hence the extra negation. */
   return (-v + (1<<9)) >> 10;
@@ -154,6 +157,11 @@
   *x = ft_face->glyph->metrics.horiBearingX -   ft_face->glyph->metrics.vertBearingX;
   *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY);
 
+  if (font->x_scale < 0)
+    *x = -*x;
+  if (font->y_scale < 0)
+    *y = -*y;
+
   return true;
 }
 
diff --git a/third_party/harfbuzz-ng/src/hb-open-file-private.hh b/third_party/harfbuzz-ng/src/hb-open-file-private.hh
index 7500c32..178bc7c 100644
--- a/third_party/harfbuzz-ng/src/hb-open-file-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-open-file-private.hh
@@ -53,7 +53,8 @@
 
 typedef struct TableRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -102,7 +103,8 @@
   }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
@@ -130,7 +132,8 @@
   inline unsigned int get_face_count (void) const { return table.len; }
   inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (table.sanitize (c, this));
   }
@@ -169,7 +172,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false);
     switch (u.header.version.major) {
@@ -233,7 +237,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false);
     switch (u.tag) {
diff --git a/third_party/harfbuzz-ng/src/hb-open-type-private.hh b/third_party/harfbuzz-ng/src/hb-open-type-private.hh
index 477d9e2..75a0f56 100644
--- a/third_party/harfbuzz-ng/src/hb-open-type-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-open-type-private.hh
@@ -179,10 +179,13 @@
   inline const char *get_name (void) { return "SANITIZE"; }
   static const unsigned int max_debug_depth = HB_DEBUG_SANITIZE;
   typedef bool return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format)
+  { return format->sanitize (this); }
   template <typename T>
   inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
   static return_t default_return_value (void) { return true; }
-  bool stop_sublookup_iteration (const return_t r HB_UNUSED) const { return false; }
+  bool stop_sublookup_iteration (const return_t r) const { return !r; }
 
   inline void init (hb_blob_t *b)
   {
@@ -270,9 +273,9 @@
   }
 
   template <typename Type, typename ValueType>
-  inline bool try_set (Type *obj, const ValueType &v) {
+  inline bool try_set (const Type *obj, const ValueType &v) {
     if (this->may_edit (obj, obj->static_size)) {
-      obj->set (v);
+      const_cast<Type *> (obj)->set (v);
       return true;
     }
     return false;
@@ -546,12 +549,6 @@
     return (v[0] <<  8)
          + (v[1]      );
   }
-  inline bool operator == (const BEInt<Type, 2>& o) const
-  {
-    return v[0] == o.v[0]
-        && v[1] == o.v[1];
-  }
-  inline bool operator != (const BEInt<Type, 2>& o) const { return !(*this == o); }
   private: uint8_t v[2];
 };
 template <typename Type>
@@ -570,13 +567,6 @@
          + (v[1] <<  8)
          + (v[2]      );
   }
-  inline bool operator == (const BEInt<Type, 3>& o) const
-  {
-    return v[0] == o.v[0]
-        && v[1] == o.v[1]
-        && v[2] == o.v[2];
-  }
-  inline bool operator != (const BEInt<Type, 3>& o) const { return !(*this == o); }
   private: uint8_t v[3];
 };
 template <typename Type>
@@ -597,14 +587,6 @@
          + (v[2] <<  8)
          + (v[3]      );
   }
-  inline bool operator == (const BEInt<Type, 4>& o) const
-  {
-    return v[0] == o.v[0]
-        && v[1] == o.v[1]
-        && v[2] == o.v[2]
-        && v[3] == o.v[3];
-  }
-  inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); }
   private: uint8_t v[4];
 };
 
@@ -614,12 +596,19 @@
 {
   inline void set (Type i) { v.set (i); }
   inline operator Type(void) const { return v; }
-  inline bool operator == (const IntType<Type,Size> &o) const { return v == o.v; }
-  inline bool operator != (const IntType<Type,Size> &o) const { return v != o.v; }
+  inline bool operator == (const IntType<Type,Size> &o) const { return (Type) v == (Type) o.v; }
+  inline bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); }
   static inline int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); }
-  inline int cmp (IntType<Type,Size> va) const { Type a = va; Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
-  inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline int cmp (Type a) const
+  {
+    Type b = v;
+    if (sizeof (Type) < sizeof (int))
+      return (int) a - (int) b;
+    else
+      return a < b ? -1 : a == b ? 0 : +1;
+  }
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (likely (c->check_struct (this)));
   }
@@ -646,7 +635,8 @@
  * 1904. The value is represented as a signed 64-bit integer. */
 struct LONGDATETIME
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (likely (c->check_struct (this)));
   }
@@ -670,7 +660,10 @@
 DEFINE_NULL_DATA (Tag, "    ");
 
 /* Glyph index number, same as uint16 (length = 16 bits) */
-typedef USHORT GlyphID;
+struct GlyphID : USHORT {
+  static inline int cmp (const GlyphID *a, const GlyphID *b) { return b->USHORT::cmp (*a); }
+  inline int cmp (hb_codepoint_t a) const { return (int) a - (int) *this; }
+};
 
 /* Script/language-system/feature index */
 struct Index : USHORT {
@@ -719,7 +712,8 @@
 {
   inline uint32_t to_int (void) const { return (major << 16) + minor; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -747,33 +741,35 @@
     return StructAtOffset<Type> (base, offset);
   }
 
-  inline Type& serialize (hb_serialize_context_t *c, void *base)
+  inline Type& serialize (hb_serialize_context_t *c, const void *base)
   {
     Type *t = c->start_embed<Type> ();
     this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
     return *t;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return TRACE_RETURN (true);
-    Type &obj = StructAtOffset<Type> (base, offset);
+    const Type &obj = StructAtOffset<Type> (base, offset);
     return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c));
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return TRACE_RETURN (true);
-    Type &obj = StructAtOffset<Type> (base, offset);
+    const Type &obj = StructAtOffset<Type> (base, offset);
     return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c));
   }
 
   /* Set the offset to Null */
-  inline bool neuter (hb_sanitize_context_t *c) {
+  inline bool neuter (hb_sanitize_context_t *c) const {
     return c->try_set (this, 0);
   }
   DEFINE_SIZE_STATIC (sizeof(OffsetType));
@@ -838,7 +834,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
@@ -853,7 +850,8 @@
 
     return TRACE_RETURN (true);
   }
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
@@ -863,7 +861,8 @@
     return TRACE_RETURN (true);
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
@@ -884,7 +883,8 @@
   }
 
   private:
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) {
+  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
   }
@@ -910,12 +910,14 @@
     return this+this->array[i];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this));
   }
   template <typename T>
-  inline bool sanitize (hb_sanitize_context_t *c, T user_data) {
+  inline bool sanitize (hb_sanitize_context_t *c, T user_data) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
@@ -949,12 +951,14 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize_shallow (hb_sanitize_context_t *c) {
+  inline bool sanitize_shallow (hb_sanitize_context_t *c) const
+  {
     return c->check_struct (this)
 	&& c->check_array (this, Type::static_size, len);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
diff --git a/third_party/harfbuzz-ng/src/hb-ot-cmap-table.hh b/third_party/harfbuzz-ng/src/hb-ot-cmap-table.hh
index d531411..0482312 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-cmap-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-cmap-table.hh
@@ -51,7 +51,8 @@
     return true;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -125,7 +126,7 @@
     return true;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c)
+  inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this)))
@@ -183,7 +184,8 @@
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -210,7 +212,8 @@
     return true;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && glyphIdArray.sanitize (c));
   }
@@ -242,7 +245,8 @@
     return true;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && groups.sanitize (c));
   }
@@ -288,7 +292,8 @@
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -309,7 +314,8 @@
     return unicodeValue.cmp (codepoint);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -348,7 +354,8 @@
     return varSelector.cmp (variation_selector);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 defaultUVS.sanitize (c, base) &&
@@ -373,7 +380,8 @@
     return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 record.sanitize (c, this));
@@ -418,7 +426,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
@@ -461,7 +470,8 @@
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 subtable.sanitize (c, base));
@@ -496,7 +506,8 @@
     return &(this+encodingRecord[result].subtable);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (version == 0) &&
diff --git a/third_party/harfbuzz-ng/src/hb-ot-head-table.hh b/third_party/harfbuzz-ng/src/hb-ot-head-table.hh
index ec4e8c9..268f133 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-head-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-head-table.hh
@@ -45,13 +45,15 @@
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_head;
 
-  inline unsigned int get_upem (void) const {
+  inline unsigned int get_upem (void) const
+  {
     unsigned int upem = unitsPerEm;
     /* If no valid head table found, assume 1000, which matches typical Type1 usage. */
     return 16 <= upem && upem <= 16384 ? upem : 1000;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
diff --git a/third_party/harfbuzz-ng/src/hb-ot-hhea-table.hh b/third_party/harfbuzz-ng/src/hb-ot-hhea-table.hh
index edc0e29..992fe55 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-hhea-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-hhea-table.hh
@@ -49,7 +49,8 @@
   static const hb_tag_t hheaTag	= HB_OT_TAG_hhea;
   static const hb_tag_t vheaTag	= HB_OT_TAG_vhea;
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
diff --git a/third_party/harfbuzz-ng/src/hb-ot-hmtx-table.hh b/third_party/harfbuzz-ng/src/hb-ot-hmtx-table.hh
index 317854c..a0e3855 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-hmtx-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-hmtx-table.hh
@@ -57,7 +57,8 @@
   static const hb_tag_t hmtxTag	= HB_OT_TAG_hmtx;
   static const hb_tag_t vmtxTag	= HB_OT_TAG_vmtx;
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh
index abd063c..3db7f57 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-common-private.hh
@@ -37,6 +37,12 @@
 namespace OT {
 
 
+#define TRACE_DISPATCH(this, format) \
+	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
+	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
+	 "format %d", (int) format);
+
+
 #define NOT_COVERED		((unsigned int) -1)
 #define MAX_NESTING_LEVEL	8
 #define MAX_CONTEXT_LENGTH	64
@@ -63,9 +69,10 @@
 
   struct sanitize_closure_t {
     hb_tag_t tag;
-    void *list_base;
+    const void *list_base;
   };
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     const sanitize_closure_t closure = {tag, base};
     return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base, &closure));
@@ -121,7 +128,8 @@
   inline const Type& operator [] (unsigned int i) const
   { return this+RecordArrayOf<Type>::operator [](i).offset; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
   }
@@ -134,7 +142,8 @@
     return g < start ? -1 : g <= end ? 0 : +1 ;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -199,7 +208,8 @@
   }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<LangSys>::sanitize_closure_t * = NULL) {
+			const Record<LangSys>::sanitize_closure_t * = NULL) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c));
   }
@@ -238,7 +248,8 @@
   inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Script>::sanitize_closure_t * = NULL) {
+			const Record<Script>::sanitize_closure_t * = NULL) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
@@ -260,7 +271,8 @@
 /* http://www.microsoft.com/typography/otspec/features_pt.htm#size */
 struct FeatureParamsSize
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
 
@@ -371,7 +383,8 @@
 /* http://www.microsoft.com/typography/otspec/features_pt.htm#ssxx */
 struct FeatureParamsStylisticSet
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     /* Right now minorVersion is at zero.  Which means, any table supports
      * the uiNameID field. */
@@ -404,7 +417,8 @@
 /* http://www.microsoft.com/typography/otspec/features_ae.htm#cv01-cv99 */
 struct FeatureParamsCharacterVariants
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 characters.sanitize (c));
@@ -444,7 +458,8 @@
 
 struct FeatureParams
 {
-  inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) {
+  inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const
+  {
     TRACE_SANITIZE (this);
     if (tag == HB_TAG ('s','i','z','e'))
       return TRACE_RETURN (u.size.sanitize (c));
@@ -486,7 +501,8 @@
   { return this+featureParams; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<Feature>::sanitize_closure_t *closure) {
+			const Record<Feature>::sanitize_closure_t *closure) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c))))
       return TRACE_RETURN (false);
@@ -561,6 +577,17 @@
 {
   inline unsigned int get_subtable_count (void) const { return subTable.len; }
 
+  template <typename SubTableType>
+  inline const SubTableType& get_subtable (unsigned int i) const
+  { return this+CastR<OffsetArrayOf<SubTableType> > (subTable)[i]; }
+
+  template <typename SubTableType>
+  inline const OffsetArrayOf<SubTableType>& get_subtables (void) const
+  { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
+  template <typename SubTableType>
+  inline OffsetArrayOf<SubTableType>& get_subtables (void)
+  { return CastR<OffsetArrayOf<SubTableType> > (subTable); }
+
   inline unsigned int get_type (void) const { return lookupType; }
 
   /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and
@@ -577,6 +604,20 @@
     return flag;
   }
 
+  template <typename SubTableType, typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    unsigned int lookup_type = get_type ();
+    TRACE_DISPATCH (this, lookup_type);
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      typename context_t::return_t r = get_subtable<SubTableType> (i).dispatch (c, lookup_type);
+      if (c->stop_sublookup_iteration (r))
+        return TRACE_RETURN (r);
+    }
+    return TRACE_RETURN (c->default_return_value ());
+  }
+
   inline bool serialize (hb_serialize_context_t *c,
 			 unsigned int lookup_type,
 			 uint32_t lookup_props,
@@ -595,18 +636,20 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
     if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
-      USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
       if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
     return TRACE_RETURN (true);
   }
 
+  private:
   USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
   USHORT	lookupFlag;		/* Lookup qualifiers */
   ArrayOf<Offset<> >
@@ -651,7 +694,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (glyphArray.sanitize (c));
   }
@@ -737,7 +781,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (rangeRecord.sanitize (c));
   }
@@ -832,7 +877,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
@@ -938,12 +984,14 @@
   private:
   inline unsigned int get_class (hb_codepoint_t glyph_id) const
   {
-    if (unlikely ((unsigned int) (glyph_id - startGlyph) < classValue.len))
-      return classValue[glyph_id - startGlyph];
+    unsigned int i = (unsigned int) (glyph_id - startGlyph);
+    if (unlikely (i < classValue.len))
+      return classValue[i];
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
   }
@@ -994,12 +1042,13 @@
   inline unsigned int get_class (hb_codepoint_t glyph_id) const
   {
     int i = rangeRecord.bsearch (glyph_id);
-    if (i != -1)
+    if (unlikely (i != -1))
       return rangeRecord[i].value;
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (rangeRecord.sanitize (c));
   }
@@ -1056,7 +1105,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
@@ -1148,7 +1198,8 @@
     return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f)));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-table.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-table.hh
index 84a5e79..7a6c04d 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gdef-table.hh
@@ -71,7 +71,8 @@
     return points.len;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
@@ -101,7 +102,8 @@
     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -127,7 +129,8 @@
       return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -150,7 +153,8 @@
            font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
@@ -178,7 +182,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
@@ -219,7 +224,8 @@
     return carets.len;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (carets.sanitize (c, this));
   }
@@ -253,7 +259,8 @@
     return lig_glyph.get_lig_carets (font, direction, glyph_id, start_offset, caret_count, caret_array);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
@@ -275,7 +282,8 @@
   inline bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this));
   }
@@ -299,7 +307,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
@@ -364,7 +373,8 @@
   inline bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
   { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef[0]).covers (set_index, glyph_id); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (version.sanitize (c) &&
 			 likely (version.major == 1) &&
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-table.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-table.hh
index f7fef52..d88f787 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gpos-table.hh
@@ -146,7 +146,8 @@
   }
 
   private:
-  inline bool sanitize_value_devices (hb_sanitize_context_t *c, void *base, Value *values) {
+  inline bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  {
     unsigned int format = *this;
 
     if (format & xPlacement) values++;
@@ -177,12 +178,14 @@
     return (format & devices) != 0;
   }
 
-  inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
+  inline bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
   }
 
-  inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
+  inline bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
+  {
     TRACE_SANITIZE (this);
     unsigned int len = get_len ();
 
@@ -200,7 +203,8 @@
   }
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
-  inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
+  inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
+  {
     TRACE_SANITIZE (this);
 
     if (!has_device ()) return TRACE_RETURN (true);
@@ -225,7 +229,8 @@
       *y = font->em_scale_y (yCoordinate);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -254,7 +259,8 @@
       *y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -282,7 +288,8 @@
 	*y += (this+yDeviceTable).get_x_delta (font);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
@@ -317,7 +324,8 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
@@ -349,7 +357,8 @@
     return this+matrixZ[row * cols + col];
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
+  {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
@@ -374,7 +383,8 @@
 {
   friend struct MarkArray;
 
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
@@ -421,7 +431,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
   }
@@ -457,9 +468,12 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
+    return TRACE_RETURN (c->check_struct (this)
+        && coverage.sanitize (c, this)
+	&& valueFormat.sanitize_value (c, this, values));
   }
 
   protected:
@@ -506,9 +520,12 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
+    return TRACE_RETURN (c->check_struct (this)
+	&& coverage.sanitize (c, this)
+	&& valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
   protected:
@@ -531,6 +548,7 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -538,16 +556,6 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -636,19 +644,20 @@
   }
 
   struct sanitize_closure_t {
-    void *base;
-    ValueFormat *valueFormats;
+    const void *base;
+    const ValueFormat *valueFormats;
     unsigned int len1; /* valueFormats[0].get_len() */
     unsigned int stride; /* 1 + len1 + len2 */
   };
 
-  inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
+  inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
+  {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
     unsigned int count = len;
-    PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
+    const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
     return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
 		      && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
@@ -681,18 +690,18 @@
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
-    hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, 1);
-    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
-
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
+    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
 
     unsigned int len1 = valueFormat1.get_len ();
@@ -752,12 +761,11 @@
   {
     TRACE_APPLY (this);
     hb_buffer_t *buffer = c->buffer;
-    hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, 1);
-    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
-
     unsigned int index = (this+coverage).get_coverage  (buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
+    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     unsigned int len1 = valueFormat1.get_len ();
@@ -781,7 +789,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
@@ -834,6 +843,7 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -841,16 +851,6 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -864,7 +864,8 @@
 {
   friend struct CursivePosFormat1;
 
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
@@ -903,12 +904,11 @@
     /* We don't handle mark glyphs here. */
     if (unlikely (_hb_glyph_info_is_mark (&buffer->cur()))) return TRACE_RETURN (false);
 
-    hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, 1);
-    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
-
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (buffer->cur().codepoint)];
     if (!this_record.exitAnchor) return TRACE_RETURN (false);
 
+    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
     if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage).get_coverage  (buffer->info[skippy_iter.idx].codepoint)];
@@ -978,7 +978,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
@@ -1001,21 +1002,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1051,7 +1044,8 @@
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
-    hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, buffer->idx, 1);
+    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
       if (!skippy_iter.prev ()) return TRACE_RETURN (false);
@@ -1069,7 +1063,8 @@
     return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) &&
 			 markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount));
@@ -1100,21 +1095,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1155,7 +1142,8 @@
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
-    hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, buffer->idx, 1);
+    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     if (!skippy_iter.prev ()) return TRACE_RETURN (false);
 
@@ -1189,7 +1177,8 @@
     return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) &&
 			 markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount));
@@ -1221,21 +1210,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1271,7 +1252,8 @@
     if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
-    hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, buffer->idx, 1);
+    hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+    skippy_iter.reset (buffer->idx, 1);
     skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
     if (!skippy_iter.prev ()) return TRACE_RETURN (false);
 
@@ -1306,7 +1288,8 @@
     return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) &&
 			 mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this)
@@ -1340,21 +1323,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1399,6 +1374,8 @@
   inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
+    /* The sub_format passed to may_dispatch is unnecessary but harmless. */
+    if (unlikely (!c->may_dispatch (this, &u.sub_format))) TRACE_RETURN (c->default_return_value ());
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.dispatch (c));
     case Pair:			return TRACE_RETURN (u.pair.dispatch (c));
@@ -1413,29 +1390,9 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
-    TRACE_SANITIZE (this);
-    if (!u.header.sub_format.sanitize (c))
-      return TRACE_RETURN (false);
-    switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.sanitize (c));
-    case Pair:			return TRACE_RETURN (u.pair.sanitize (c));
-    case Cursive:		return TRACE_RETURN (u.cursive.sanitize (c));
-    case MarkBase:		return TRACE_RETURN (u.markBase.sanitize (c));
-    case MarkLig:		return TRACE_RETURN (u.markLig.sanitize (c));
-    case MarkMark:		return TRACE_RETURN (u.markMark.sanitize (c));
-    case Context:		return TRACE_RETURN (u.context.sanitize (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
-    case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
-    default:			return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
-  struct {
-    USHORT		sub_format;
-  } header;
+  USHORT		sub_format;
   SinglePos		single;
   PairPos		pair;
   CursivePos		cursive;
@@ -1447,48 +1404,37 @@
   ExtensionPos		extension;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, header.sub_format);
+  DEFINE_SIZE_UNION (2, sub_format);
 };
 
 
 struct PosLookup : Lookup
 {
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
-  { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
+  { return Lookup::get_subtable<PosLookupSubTable> (i); }
 
   inline bool is_reverse (void) const
   {
     return false;
   }
 
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    return TRACE_RETURN (dispatch (c));
+  }
+
   inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    c->set_recurse_func (NULL);
     return TRACE_RETURN (dispatch (c));
   }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
-    hb_get_coverage_context_t c;
-    const Coverage *last = NULL;
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++) {
-      const Coverage *coverage = &get_subtable (i).dispatch (&c, get_type ());
-      if (coverage != last) {
-        coverage->add_coverage (glyphs);
-        last = coverage;
-      }
-    }
-  }
-
-  inline bool apply_once (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
-      return TRACE_RETURN (false);
-    return TRACE_RETURN (dispatch (c));
+    hb_add_coverage_context_t<set_t> c (glyphs);
+    dispatch (&c);
   }
 
   static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
@@ -1498,23 +1444,14 @@
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
-  {
-    unsigned int lookup_type = get_type ();
-    TRACE_DISPATCH (this, lookup_type);
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++) {
-      typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
-      if (c->stop_sublookup_iteration (r))
-        return TRACE_RETURN (r);
-    }
-    return TRACE_RETURN (c->default_return_value ());
-  }
+  { return Lookup::dispatch<PosLookupSubTable> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
-    OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
-    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
+    const OffsetArrayOf<PosLookupSubTable> &list = get_subtables<PosLookupSubTable> ();
+    return TRACE_RETURN (dispatch (c));
   }
 };
 
@@ -1534,10 +1471,11 @@
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer);
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
-    OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
+    const OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
@@ -1632,8 +1570,8 @@
   const PosLookup &l = gpos.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   c->set_lookup (l);
-  bool ret = l.apply_once (c);
-  c->lookup_props = saved_lookup_props;
+  bool ret = l.dispatch (c);
+  c->set_lookup_props (saved_lookup_props);
   return ret;
 }
 
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-table.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-table.hh
index 5d67be0..ebe4c9e 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gsub-table.hh
@@ -97,7 +97,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
@@ -173,7 +174,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
@@ -223,6 +225,7 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -230,16 +233,6 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -312,7 +305,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (substitute.sanitize (c));
   }
@@ -384,7 +378,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
@@ -423,21 +418,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -535,7 +522,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
@@ -574,21 +562,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -686,7 +666,8 @@
   }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
   }
@@ -764,7 +745,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (ligature.sanitize (c, this));
   }
@@ -848,7 +830,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
@@ -890,21 +873,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1017,14 +992,15 @@
     return TRACE_RETURN (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
       return TRACE_RETURN (false);
-    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!lookahead.sanitize (c, this))
       return TRACE_RETURN (false);
-    ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
+    const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
     return TRACE_RETURN (substitute.sanitize (c));
   }
 
@@ -1054,21 +1030,13 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT				format;		/* Format identifier */
@@ -1101,6 +1069,8 @@
   inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
+    /* The sub_format passed to may_dispatch is unnecessary but harmless. */
+    if (unlikely (!c->may_dispatch (this, &u.sub_format))) TRACE_RETURN (c->default_return_value ());
     switch (lookup_type) {
     case Single:		return TRACE_RETURN (u.single.dispatch (c));
     case Multiple:		return TRACE_RETURN (u.multiple.dispatch (c));
@@ -1114,28 +1084,9 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
-    TRACE_SANITIZE (this);
-    if (!u.header.sub_format.sanitize (c))
-      return TRACE_RETURN (false);
-    switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.sanitize (c));
-    case Multiple:		return TRACE_RETURN (u.multiple.sanitize (c));
-    case Alternate:		return TRACE_RETURN (u.alternate.sanitize (c));
-    case Ligature:		return TRACE_RETURN (u.ligature.sanitize (c));
-    case Context:		return TRACE_RETURN (u.context.sanitize (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
-    case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
-    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
-    default:			return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
-  struct {
-    USHORT			sub_format;
-  } header;
+  USHORT			sub_format;
   SingleSubst			single;
   MultipleSubst			multiple;
   AlternateSubst		alternate;
@@ -1146,14 +1097,14 @@
   ReverseChainSingleSubst	reverseChainContextSingle;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, header.sub_format);
+  DEFINE_SIZE_UNION (2, sub_format);
 };
 
 
 struct SubstLookup : Lookup
 {
   inline const SubstLookupSubTable& get_subtable (unsigned int i) const
-  { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
+  { return Lookup::get_subtable<SubstLookupSubTable> (i); }
 
   inline static bool lookup_type_is_reverse (unsigned int lookup_type)
   { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
@@ -1166,6 +1117,12 @@
     return lookup_type_is_reverse (type);
   }
 
+  inline bool apply (hb_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    return TRACE_RETURN (dispatch (c));
+  }
+
   inline hb_closure_context_t::return_t closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
@@ -1183,39 +1140,24 @@
   template <typename set_t>
   inline void add_coverage (set_t *glyphs) const
   {
-    hb_get_coverage_context_t c;
-    const Coverage *last = NULL;
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++) {
-      const Coverage *coverage = &get_subtable (i).dispatch (&c, get_type ());
-      if (coverage != last) {
-        coverage->add_coverage (glyphs);
-        last = coverage;
-      }
-    }
+    hb_add_coverage_context_t<set_t> c (glyphs);
+    dispatch (&c);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c, const hb_set_digest_t *digest) const
+  inline bool would_apply (hb_would_apply_context_t *c,
+			   const hb_ot_layout_lookup_accelerator_t *accel) const
   {
     TRACE_WOULD_APPLY (this);
     if (unlikely (!c->len))  return TRACE_RETURN (false);
-    if (!digest->may_have (c->glyphs[0]))  return TRACE_RETURN (false);
+    if (!accel->may_have (c->glyphs[0]))  return TRACE_RETURN (false);
       return TRACE_RETURN (dispatch (c));
   }
 
-  inline bool apply_once (hb_apply_context_t *c) const
-  {
-    TRACE_APPLY (this);
-    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
-      return TRACE_RETURN (false);
-    return TRACE_RETURN (dispatch (c));
-  }
-
   static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
 
   inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c,
 						  unsigned int i)
-  { return CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i].serialize (c, this); }
+  { return get_subtables<SubstLookupSubTable> ()[i].serialize (c, this); }
 
   inline bool serialize_single (hb_serialize_context_t *c,
 				uint32_t lookup_props,
@@ -1274,24 +1216,14 @@
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
-  {
-    unsigned int lookup_type = get_type ();
-    TRACE_DISPATCH (this, lookup_type);
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++) {
-      typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
-      if (c->stop_sublookup_iteration (r))
-        return TRACE_RETURN (r);
-    }
-    return TRACE_RETURN (c->default_return_value ());
-  }
+  { return Lookup::dispatch<SubstLookupSubTable> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c)
+  inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
-    OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
-    if (unlikely (!list.sanitize (c, this, get_type ()))) return TRACE_RETURN (false);
+    const OffsetArrayOf<SubstLookupSubTable> &list = get_subtables<SubstLookupSubTable> ();
+    if (unlikely (!dispatch (c))) return TRACE_RETURN (false);
 
     if (unlikely (get_type () == SubstLookupSubTable::Extension))
     {
@@ -1324,10 +1256,11 @@
   static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer);
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
-    OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
+    const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
@@ -1362,7 +1295,7 @@
 {
   unsigned int type = get_type ();
   if (unlikely (type == SubstLookupSubTable::Extension))
-    return CastR<ExtensionSubst> (get_subtable<SubstLookupSubTable>()).is_reverse ();
+    return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse ();
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
@@ -1380,8 +1313,8 @@
   const SubstLookup &l = gsub.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   c->set_lookup (l);
-  bool ret = l.apply_once (c);
-  c->lookup_props = saved_lookup_props;
+  bool ret = l.dispatch (c);
+  c->set_lookup_props (saved_lookup_props);
   return ret;
 }
 
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh
index 57fc1e0..cbc6840 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-gsubgpos-private.hh
@@ -37,12 +37,6 @@
 namespace OT {
 
 
-
-#define TRACE_DISPATCH(this, format) \
-	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
-	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
-	 "format %d", (int) format);
-
 #ifndef HB_DEBUG_CLOSURE
 #define HB_DEBUG_CLOSURE (HB_DEBUG+0)
 #endif
@@ -58,6 +52,8 @@
   static const unsigned int max_debug_depth = HB_DEBUG_CLOSURE;
   typedef hb_void_t return_t;
   typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
   template <typename T>
   inline return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; }
   static return_t default_return_value (void) { return HB_VOID; }
@@ -107,6 +103,8 @@
   inline const char *get_name (void) { return "WOULD_APPLY"; }
   static const unsigned int max_debug_depth = HB_DEBUG_WOULD_APPLY;
   typedef bool return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
   template <typename T>
   inline return_t dispatch (const T &obj) { return obj.would_apply (this); }
   static return_t default_return_value (void) { return false; }
@@ -146,6 +144,8 @@
   static const unsigned int max_debug_depth = HB_DEBUG_COLLECT_GLYPHS;
   typedef hb_void_t return_t;
   typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index);
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
   template <typename T>
   inline return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; }
   static return_t default_return_value (void) { return HB_VOID; }
@@ -232,18 +232,28 @@
 #define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
 #endif
 
-struct hb_get_coverage_context_t
+template <typename set_t>
+struct hb_add_coverage_context_t
 {
   inline const char *get_name (void) { return "GET_COVERAGE"; }
   static const unsigned int max_debug_depth = HB_DEBUG_GET_COVERAGE;
   typedef const Coverage &return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
   template <typename T>
   inline return_t dispatch (const T &obj) { return obj.get_coverage (); }
   static return_t default_return_value (void) { return Null(Coverage); }
+  bool stop_sublookup_iteration (return_t r) const
+  {
+    r.add_coverage (set);
+    return false;
+  }
 
-  hb_get_coverage_context_t (void) :
+  hb_add_coverage_context_t (set_t *set_) :
+			    set (set_),
 			    debug_depth (0) {}
 
+  set_t *set;
   unsigned int debug_depth;
 };
 
@@ -260,61 +270,6 @@
 
 struct hb_apply_context_t
 {
-  inline const char *get_name (void) { return "APPLY"; }
-  static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
-  typedef bool return_t;
-  typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
-  template <typename T>
-  inline return_t dispatch (const T &obj) { return obj.apply (this); }
-  static return_t default_return_value (void) { return false; }
-  bool stop_sublookup_iteration (return_t r) const { return r; }
-  return_t recurse (unsigned int lookup_index)
-  {
-    if (unlikely (nesting_level_left == 0 || !recurse_func))
-      return default_return_value ();
-
-    nesting_level_left--;
-    bool ret = recurse_func (this, lookup_index);
-    nesting_level_left++;
-    return ret;
-  }
-
-  unsigned int table_index; /* GSUB/GPOS */
-  hb_font_t *font;
-  hb_face_t *face;
-  hb_buffer_t *buffer;
-  hb_direction_t direction;
-  hb_mask_t lookup_mask;
-  bool auto_zwj;
-  recurse_func_t recurse_func;
-  unsigned int nesting_level_left;
-  unsigned int lookup_props;
-  const GDEF &gdef;
-  bool has_glyph_classes;
-  unsigned int debug_depth;
-
-
-  hb_apply_context_t (unsigned int table_index_,
-		      hb_font_t *font_,
-		      hb_buffer_t *buffer_) :
-			table_index (table_index_),
-			font (font_), face (font->face), buffer (buffer_),
-			direction (buffer_->props.direction),
-			lookup_mask (1),
-			auto_zwj (true),
-			recurse_func (NULL),
-			nesting_level_left (MAX_NESTING_LEVEL),
-			lookup_props (0),
-			gdef (*hb_ot_layout_from_face (face)->gdef),
-			has_glyph_classes (gdef.has_glyph_classes ()),
-			debug_depth (0) {}
-
-  inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; }
-  inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; }
-  inline void set_recurse_func (recurse_func_t func) { recurse_func = func; }
-  inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
-  inline void set_lookup (const Lookup &l) { lookup_props = l.get_props (); }
-
   struct matcher_t
   {
     inline matcher_t (void) :
@@ -390,29 +345,24 @@
     const void *match_data;
   };
 
-  struct skipping_forward_iterator_t
+  struct skipping_iterator_t
   {
-    inline skipping_forward_iterator_t (hb_apply_context_t *c_,
-					unsigned int start_index_,
-					unsigned int num_items_,
-					bool context_match = false) :
-					 idx (start_index_),
-					 c (c_),
-					 match_glyph_data (NULL),
-					 num_items (num_items_),
-					 end (c->buffer->len)
+    inline void init (hb_apply_context_t *c_, bool context_match = false)
     {
+      c = c_;
+      match_glyph_data = NULL,
+      matcher.set_match_func (NULL, NULL);
       matcher.set_lookup_props (c->lookup_props);
       /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
       matcher.set_ignore_zwnj (context_match || c->table_index == 1);
       /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
       matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj);
-      if (!context_match)
-	matcher.set_mask (c->lookup_mask);
-      matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
+      matcher.set_mask (context_match ? -1 : c->lookup_mask);
     }
-    inline void set_lookup_props (unsigned int lookup_props) { matcher.set_lookup_props (lookup_props); }
-    inline void set_syllable (unsigned int syllable) { matcher.set_syllable (syllable); }
+    inline void set_lookup_props (unsigned int lookup_props)
+    {
+      matcher.set_lookup_props (lookup_props);
+    }
     inline void set_match_func (matcher_t::match_func_t match_func,
 				const void *match_data,
 				const USHORT glyph_data[])
@@ -421,12 +371,21 @@
       match_glyph_data = glyph_data;
     }
 
-    inline bool has_no_chance (void) const { return unlikely (num_items && idx + num_items >= end); }
+    inline void reset (unsigned int start_index_,
+		       unsigned int num_items_)
+    {
+      idx = start_index_;
+      num_items = num_items_;
+      end = c->buffer->len;
+      matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
+    }
+
     inline void reject (void) { num_items++; match_glyph_data--; }
+
     inline bool next (void)
     {
       assert (num_items > 0);
-      while (!has_no_chance ())
+      while (idx + num_items < end)
       {
 	idx++;
 	const hb_glyph_info_t &info = c->buffer->info[idx];
@@ -450,53 +409,10 @@
       }
       return false;
     }
-
-    unsigned int idx;
-    protected:
-    hb_apply_context_t *c;
-    matcher_t matcher;
-    const USHORT *match_glyph_data;
-
-    unsigned int num_items;
-    unsigned int end;
-  };
-
-  struct skipping_backward_iterator_t
-  {
-    inline skipping_backward_iterator_t (hb_apply_context_t *c_,
-					 unsigned int start_index_,
-					 unsigned int num_items_,
-					 bool context_match = false) :
-					  idx (start_index_),
-					  c (c_),
-					  match_glyph_data (NULL),
-					  num_items (num_items_)
-    {
-      matcher.set_lookup_props (c->lookup_props);
-      /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
-      matcher.set_ignore_zwnj (context_match || c->table_index == 1);
-      /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
-      matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj);
-      if (!context_match)
-	matcher.set_mask (c->lookup_mask);
-      matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
-    }
-    inline void set_lookup_props (unsigned int lookup_props) { matcher.set_lookup_props (lookup_props); }
-    inline void set_syllable (unsigned int syllable) { matcher.set_syllable (syllable); }
-    inline void set_match_func (matcher_t::match_func_t match_func,
-				const void *match_data,
-				const USHORT glyph_data[])
-    {
-      matcher.set_match_func (match_func, match_data);
-      match_glyph_data = glyph_data;
-    }
-
-    inline bool has_no_chance (void) const { return unlikely (idx < num_items); }
-    inline void reject (void) { num_items++; }
     inline bool prev (void)
     {
       assert (num_items > 0);
-      while (!has_no_chance ())
+      while (idx >= num_items)
       {
 	idx--;
 	const hb_glyph_info_t &info = c->buffer->out_info[idx];
@@ -528,8 +444,75 @@
     const USHORT *match_glyph_data;
 
     unsigned int num_items;
+    unsigned int end;
   };
 
+
+  inline const char *get_name (void) { return "APPLY"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
+  typedef bool return_t;
+  typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return obj.apply (this); }
+  static return_t default_return_value (void) { return false; }
+  bool stop_sublookup_iteration (return_t r) const { return r; }
+  return_t recurse (unsigned int lookup_index)
+  {
+    if (unlikely (nesting_level_left == 0 || !recurse_func))
+      return default_return_value ();
+
+    nesting_level_left--;
+    bool ret = recurse_func (this, lookup_index);
+    nesting_level_left++;
+    return ret;
+  }
+
+  unsigned int table_index; /* GSUB/GPOS */
+  hb_font_t *font;
+  hb_face_t *face;
+  hb_buffer_t *buffer;
+  hb_direction_t direction;
+  hb_mask_t lookup_mask;
+  bool auto_zwj;
+  recurse_func_t recurse_func;
+  unsigned int nesting_level_left;
+  unsigned int lookup_props;
+  const GDEF &gdef;
+  bool has_glyph_classes;
+  skipping_iterator_t iter_input, iter_context;
+  unsigned int debug_depth;
+
+
+  hb_apply_context_t (unsigned int table_index_,
+		      hb_font_t *font_,
+		      hb_buffer_t *buffer_) :
+			table_index (table_index_),
+			font (font_), face (font->face), buffer (buffer_),
+			direction (buffer_->props.direction),
+			lookup_mask (1),
+			auto_zwj (true),
+			recurse_func (NULL),
+			nesting_level_left (MAX_NESTING_LEVEL),
+			lookup_props (0),
+			gdef (*hb_ot_layout_from_face (face)->gdef),
+			has_glyph_classes (gdef.has_glyph_classes ()),
+			iter_input (),
+			iter_context (),
+			debug_depth (0) {}
+
+  inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; }
+  inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; }
+  inline void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+  inline void set_lookup (const Lookup &l) { set_lookup_props (l.get_props ()); }
+  inline void set_lookup_props (unsigned int lookup_props_)
+  {
+    lookup_props = lookup_props_;
+    iter_input.init (this, false);
+    iter_context.init (this, true);
+  }
+
   inline bool
   match_properties_mark (hb_codepoint_t  glyph,
 			 unsigned int    glyph_props,
@@ -741,9 +724,9 @@
 
   hb_buffer_t *buffer = c->buffer;
 
-  hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, buffer->idx, count - 1);
+  hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+  skippy_iter.reset (buffer->idx, count - 1);
   skippy_iter.set_match_func (match_func, match_data, input);
-  if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
   /*
    * This is perhaps the trickiest part of OpenType...  Remarks:
@@ -910,9 +893,9 @@
 {
   TRACE_APPLY (NULL);
 
-  hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true);
+  hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
+  skippy_iter.reset (c->buffer->backtrack_len (), count);
   skippy_iter.set_match_func (match_func, match_data, backtrack);
-  if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.prev ())
@@ -930,9 +913,9 @@
 {
   TRACE_APPLY (NULL);
 
-  hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true);
+  hb_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
+  skippy_iter.reset (c->buffer->idx + offset - 1, count);
   skippy_iter.set_match_func (match_func, match_data, lookahead);
-  if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
   for (unsigned int i = 0; i < count; i++)
     if (!skippy_iter.next ())
@@ -945,7 +928,8 @@
 
 struct LookupRecord
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this));
   }
@@ -1168,7 +1152,8 @@
   }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return inputCount.sanitize (c)
 	&& lookupCount.sanitize (c)
@@ -1232,7 +1217,8 @@
     return TRACE_RETURN (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (rule.sanitize (c, this));
   }
@@ -1314,7 +1300,8 @@
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
@@ -1406,7 +1393,8 @@
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
@@ -1494,7 +1482,8 @@
     return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
@@ -1502,7 +1491,7 @@
     if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
       if (!coverageZ[i].sanitize (c, this)) return TRACE_RETURN (false);
-    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count);
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count);
     return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
@@ -1526,6 +1515,7 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -1534,17 +1524,6 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    case 3: return TRACE_RETURN (u.format3.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
@@ -1726,14 +1705,15 @@
 						     lookup.array, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
-    HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
+    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     if (!input.sanitize (c)) return TRACE_RETURN (false);
-    ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
+    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
-    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return TRACE_RETURN (lookup.sanitize (c));
   }
 
@@ -1795,7 +1775,8 @@
     return TRACE_RETURN (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (rule.sanitize (c, this));
   }
@@ -1874,7 +1855,8 @@
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
@@ -1984,7 +1966,8 @@
     return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
 			 inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
@@ -2105,15 +2088,16 @@
 						     lookup.len, lookup.array, lookup_context));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
-    OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!input.sanitize (c, this)) return TRACE_RETURN (false);
     if (!input.len) return TRACE_RETURN (false); /* To be consistent with Context. */
-    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
-    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
+    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
     return TRACE_RETURN (lookup.sanitize (c));
   }
 
@@ -2144,6 +2128,7 @@
   inline typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
     case 1: return TRACE_RETURN (c->dispatch (u.format1));
     case 2: return TRACE_RETURN (c->dispatch (u.format2));
@@ -2152,17 +2137,6 @@
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    case 2: return TRACE_RETURN (u.format2.sanitize (c));
-    case 3: return TRACE_RETURN (u.format3.sanitize (c));
-    default:return TRACE_RETURN (true);
-    }
-  }
-
   protected:
   union {
   USHORT		format;	/* Format identifier */
@@ -2173,14 +2147,32 @@
 };
 
 
+template <typename T>
 struct ExtensionFormat1
 {
   inline unsigned int get_type (void) const { return extensionLookupType; }
-  inline unsigned int get_offset (void) const { return extensionOffset; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  template <typename X>
+  inline const X& get_subtable (void) const
+  {
+    unsigned int offset = extensionOffset;
+    if (unlikely (!offset)) return Null(typename T::LookupSubTable);
+    return StructAtOffset<typename T::LookupSubTable> (this, offset);
+  }
+
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this, format);
+    if (unlikely (!c->may_dispatch (this, this))) TRACE_RETURN (c->default_return_value ());
+    return get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ());
+  }
+
+  /* This is called from may_dispatch() above with hb_sanitize_context_t. */
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
-    return TRACE_RETURN (c->check_struct (this));
+    return TRACE_RETURN (c->check_struct (this) && extensionOffset != 0);
   }
 
   protected:
@@ -2204,49 +2196,30 @@
     default:return 0;
     }
   }
-  inline unsigned int get_offset (void) const
-  {
-    switch (u.format) {
-    case 1: return u.format1.get_offset ();
-    default:return 0;
-    }
-  }
-
   template <typename X>
   inline const X& get_subtable (void) const
   {
-    unsigned int offset = get_offset ();
-    if (unlikely (!offset)) return Null(typename T::LookupSubTable);
-    return StructAtOffset<typename T::LookupSubTable> (this, offset);
+    switch (u.format) {
+    case 1: return u.format1.template get_subtable<typename T::LookupSubTable> ();
+    default:return Null(typename T::LookupSubTable);
+    }
   }
 
   template <typename context_t>
   inline typename context_t::return_t dispatch (context_t *c) const
   {
-    return get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ());
-  }
-
-  inline bool sanitize_self (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
+    TRACE_DISPATCH (this, u.format);
+    if (unlikely (!c->may_dispatch (this, &u.format))) TRACE_RETURN (c->default_return_value ());
     switch (u.format) {
-    case 1: return TRACE_RETURN (u.format1.sanitize (c));
-    default:return TRACE_RETURN (true);
+    case 1: return TRACE_RETURN (u.format1.dispatch (c));
+    default:return TRACE_RETURN (c->default_return_value ());
     }
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
-    TRACE_SANITIZE (this);
-    if (!sanitize_self (c)) return TRACE_RETURN (false);
-    unsigned int offset = get_offset ();
-    if (unlikely (!offset)) return TRACE_RETURN (true);
-    return TRACE_RETURN (StructAtOffset<typename T::LookupSubTable> (this, offset).sanitize (c, get_type ()));
-  }
-
   protected:
   union {
   USHORT		format;		/* Format identifier */
-  ExtensionFormat1	format1;
+  ExtensionFormat1<T>	format1;
   } u;
 };
 
@@ -2291,7 +2264,8 @@
   inline const Lookup& get_lookup (unsigned int i) const
   { return (this+lookupList)[i]; }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
 			 scriptList.sanitize (c, this) &&
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-jstf-table.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-jstf-table.hh
index 67a6df5..739dfd9 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-jstf-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-jstf-table.hh
@@ -54,7 +54,8 @@
 
 struct JstfPriority
 {
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 shrinkageEnableGSUB.sanitize (c, this) &&
@@ -123,7 +124,8 @@
 struct JstfLangSys : OffsetListOf<JstfPriority>
 {
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<JstfLangSys>::sanitize_closure_t * = NULL) {
+			const Record<JstfLangSys>::sanitize_closure_t * = NULL) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (OffsetListOf<JstfPriority>::sanitize (c));
   }
@@ -163,7 +165,8 @@
   inline const JstfLangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
 
   inline bool sanitize (hb_sanitize_context_t *c,
-			const Record<JstfScript>::sanitize_closure_t * = NULL) {
+			const Record<JstfScript>::sanitize_closure_t * = NULL) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (extenderGlyphs.sanitize (c, this) &&
 			 defaultLangSys.sanitize (c, this) &&
@@ -206,7 +209,8 @@
   inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
   { return scriptList.find_index (tag, index); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
 			 scriptList.sanitize (c, this));
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh b/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh
index 3f7c858..47fecd2 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout-private.hh
@@ -130,6 +130,11 @@
   {
   }
 
+  inline bool may_have (hb_codepoint_t g) const {
+    return digest.may_have (g);
+  }
+
+  private:
   hb_set_digest_t digest;
 };
 
diff --git a/third_party/harfbuzz-ng/src/hb-ot-layout.cc b/third_party/harfbuzz-ng/src/hb-ot-layout.cc
index 602b94e..b1e69e8 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-layout.cc
+++ b/third_party/harfbuzz-ng/src/hb-ot-layout.cc
@@ -699,7 +699,7 @@
 
   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
 
-  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest);
+  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
 }
 
 void
@@ -829,26 +829,83 @@
 };
 
 
-template <typename Lookup>
-static inline bool apply_once (OT::hb_apply_context_t *c,
-			       const Lookup &lookup)
+template <typename Obj>
+static inline bool
+apply_forward (OT::hb_apply_context_t *c,
+	       const Obj &obj,
+	       const hb_ot_layout_lookup_accelerator_t &accel)
 {
-  if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
-    return false;
-  return lookup.dispatch (c);
+  bool ret = false;
+  hb_buffer_t *buffer = c->buffer;
+  while (buffer->idx < buffer->len)
+  {
+    if (accel.may_have (buffer->cur().codepoint) &&
+	(buffer->cur().mask & c->lookup_mask) &&
+	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
+	obj.apply (c))
+      ret = true;
+    else
+      buffer->next_glyph ();
+  }
+  return ret;
 }
 
-template <typename Proxy>
+template <typename Obj>
 static inline bool
+apply_backward (OT::hb_apply_context_t *c,
+		const Obj &obj,
+		const hb_ot_layout_lookup_accelerator_t &accel)
+{
+  bool ret = false;
+  hb_buffer_t *buffer = c->buffer;
+  do
+  {
+    if (accel.may_have (buffer->cur().codepoint) &&
+	(buffer->cur().mask & c->lookup_mask) &&
+	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
+	obj.apply (c))
+      ret = true;
+    /* The reverse lookup doesn't "advance" cursor (for good reason). */
+    buffer->idx--;
+
+  }
+  while ((int) buffer->idx >= 0);
+  return ret;
+}
+
+struct hb_apply_forward_context_t
+{
+  inline const char *get_name (void) { return "APPLY_FORWARD"; }
+  static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
+  typedef bool return_t;
+  template <typename T, typename F>
+  inline bool may_dispatch (const T *obj, const F *format) { return true; }
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); }
+  static return_t default_return_value (void) { return false; }
+  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; }
+
+  hb_apply_forward_context_t (OT::hb_apply_context_t *c_,
+			      const hb_ot_layout_lookup_accelerator_t &accel_) :
+				c (c_),
+				accel (accel_),
+				debug_depth (0) {}
+
+  OT::hb_apply_context_t *c;
+  const hb_ot_layout_lookup_accelerator_t &accel;
+  unsigned int debug_depth;
+};
+
+template <typename Proxy>
+static inline void
 apply_string (OT::hb_apply_context_t *c,
 	      const typename Proxy::Lookup &lookup,
 	      const hb_ot_layout_lookup_accelerator_t &accel)
 {
-  bool ret = false;
   hb_buffer_t *buffer = c->buffer;
 
   if (unlikely (!buffer->len || !c->lookup_mask))
-    return false;
+    return;
 
   c->set_lookup (lookup);
 
@@ -859,21 +916,20 @@
       buffer->clear_output ();
     buffer->idx = 0;
 
-    while (buffer->idx < buffer->len)
+    bool ret;
+    if (lookup.get_subtable_count () == 1)
     {
-      if (accel.digest.may_have (buffer->cur().codepoint) &&
-	  (buffer->cur().mask & c->lookup_mask) &&
-	  apply_once (c, lookup))
-	ret = true;
-      else
-	buffer->next_glyph ();
+      hb_apply_forward_context_t c_forward (c, accel);
+      ret = lookup.dispatch (&c_forward);
     }
+    else
+      ret = apply_forward (c, lookup, accel);
     if (ret)
     {
       if (!Proxy::inplace)
 	buffer->swap_buffers ();
       else
-        assert (!buffer->has_separate_output ());
+	assert (!buffer->has_separate_output ());
     }
   }
   else
@@ -882,20 +938,9 @@
     if (Proxy::table_index == 0)
       buffer->remove_output ();
     buffer->idx = buffer->len - 1;
-    do
-    {
-      if (accel.digest.may_have (buffer->cur().codepoint) &&
-	  (buffer->cur().mask & c->lookup_mask) &&
-	  apply_once (c, lookup))
-	ret = true;
-      /* The reverse lookup doesn't "advance" cursor (for good reason). */
-      buffer->idx--;
 
-    }
-    while ((int) buffer->idx >= 0);
+    apply_backward (c, lookup, accel);
   }
-
-  return ret;
 }
 
 template <typename Proxy>
diff --git a/third_party/harfbuzz-ng/src/hb-ot-maxp-table.hh b/third_party/harfbuzz-ng/src/hb-ot-maxp-table.hh
index b1f8328..0d9a0fa 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-maxp-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-maxp-table.hh
@@ -43,11 +43,13 @@
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_maxp;
 
-  inline unsigned int get_num_glyphs (void) const {
+  inline unsigned int get_num_glyphs (void) const
+  {
     return numGlyphs;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000u)));
diff --git a/third_party/harfbuzz-ng/src/hb-ot-name-table.hh b/third_party/harfbuzz-ng/src/hb-ot-name-table.hh
index 31d9fac..21450c6 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-name-table.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-name-table.hh
@@ -56,7 +56,8 @@
     return 0;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c, void *base) {
+  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
+  {
     TRACE_SANITIZE (this);
     /* We can check from base all the way up to the end of string... */
     return TRACE_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset));
@@ -101,7 +102,7 @@
   inline unsigned int get_size (void) const
   { return min_size + count * nameRecord[0].min_size; }
 
-  inline bool sanitize_records (hb_sanitize_context_t *c) {
+  inline bool sanitize_records (hb_sanitize_context_t *c) const {
     TRACE_SANITIZE (this);
     char *string_pool = (char *) this + stringOffset;
     unsigned int _count = count;
@@ -110,7 +111,8 @@
     return TRACE_RETURN (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 likely (format == 0 || format == 1) &&
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-win1256.hh b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-win1256.hh
index 3a20b50..8edd3ba 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-win1256.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -133,7 +133,6 @@
  */
 
 #define OT_LOOKUP_TYPE_SUBST_SINGLE	1u
-#define OT_LOOKUP_TYPE_SUBST_MULTIPLE	2u
 #define OT_LOOKUP_TYPE_SUBST_LIGATURE	4u
 
 #define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-myanmar-machine.hh b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-myanmar-machine.hh
index 83ade21..29fdf9a 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-myanmar-machine.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -1,5 +1,5 @@
 
-#line 1 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 1 "hb-ot-shape-complex-myanmar-machine.rl"
 /*
  * Copyright © 2011,2012  Google, Inc.
  *
@@ -32,7 +32,7 @@
 #include "hb-private.hh"
 
 
-#line 36 "../../src/hb-ot-shape-complex-myanmar-machine.hh"
+#line 36 "hb-ot-shape-complex-myanmar-machine.hh"
 static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
 	1u, 31u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
 	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, 
@@ -261,11 +261,11 @@
 static const int myanmar_syllable_machine_en_main = 0;
 
 
-#line 36 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 36 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
 
-#line 93 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 93 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
 #define found_syllable(syllable_type) \
@@ -285,7 +285,7 @@
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 289 "../../src/hb-ot-shape-complex-myanmar-machine.hh"
+#line 289 "hb-ot-shape-complex-myanmar-machine.hh"
 	{
 	cs = myanmar_syllable_machine_start;
 	ts = 0;
@@ -293,7 +293,7 @@
 	act = 0;
 	}
 
-#line 114 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 114 "hb-ot-shape-complex-myanmar-machine.rl"
 
 
   p = 0;
@@ -302,7 +302,7 @@
   unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 306 "../../src/hb-ot-shape-complex-myanmar-machine.hh"
+#line 306 "hb-ot-shape-complex-myanmar-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -316,7 +316,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 320 "../../src/hb-ot-shape-complex-myanmar-machine.hh"
+#line 320 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
@@ -335,38 +335,38 @@
 
 	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
 	case 7:
-#line 85 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 85 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (consonant_syllable); }}
 	break;
 	case 5:
-#line 86 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
 	break;
 	case 10:
-#line 87 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 87 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (punctuation_cluster); }}
 	break;
 	case 4:
-#line 88 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 88 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (broken_cluster); }}
 	break;
 	case 3:
-#line 89 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
 	break;
 	case 6:
-#line 85 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 85 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (consonant_syllable); }}
 	break;
 	case 8:
-#line 88 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 88 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 9:
-#line 89 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (non_myanmar_cluster); }}
 	break;
-#line 370 "../../src/hb-ot-shape-complex-myanmar-machine.hh"
+#line 370 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 _again:
@@ -375,7 +375,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 379 "../../src/hb-ot-shape-complex-myanmar-machine.hh"
+#line 379 "hb-ot-shape-complex-myanmar-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -391,7 +391,7 @@
 
 	}
 
-#line 123 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 123 "hb-ot-shape-complex-myanmar-machine.rl"
 
 }
 
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-sea-machine.hh b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-sea-machine.hh
index 789e4d6..15b862f 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-sea-machine.hh
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-sea-machine.hh
@@ -1,5 +1,5 @@
 
-#line 1 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 1 "hb-ot-shape-complex-sea-machine.rl"
 /*
  * Copyright © 2011,2012,2013  Google, Inc.
  *
@@ -32,7 +32,7 @@
 #include "hb-private.hh"
 
 
-#line 36 "../../src/hb-ot-shape-complex-sea-machine.hh"
+#line 36 "hb-ot-shape-complex-sea-machine.hh"
 static const unsigned char _sea_syllable_machine_trans_keys[] = {
 	1u, 1u, 1u, 1u, 1u, 29u, 3u, 29u, 3u, 29u, 1u, 1u, 0
 };
@@ -89,11 +89,11 @@
 static const int sea_syllable_machine_en_main = 2;
 
 
-#line 36 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 36 "hb-ot-shape-complex-sea-machine.rl"
 
 
 
-#line 67 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 67 "hb-ot-shape-complex-sea-machine.rl"
 
 
 #define found_syllable(syllable_type) \
@@ -113,7 +113,7 @@
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 117 "../../src/hb-ot-shape-complex-sea-machine.hh"
+#line 117 "hb-ot-shape-complex-sea-machine.hh"
 	{
 	cs = sea_syllable_machine_start;
 	ts = 0;
@@ -121,7 +121,7 @@
 	act = 0;
 	}
 
-#line 88 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 88 "hb-ot-shape-complex-sea-machine.rl"
 
 
   p = 0;
@@ -130,7 +130,7 @@
   unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 134 "../../src/hb-ot-shape-complex-sea-machine.hh"
+#line 134 "hb-ot-shape-complex-sea-machine.hh"
 	{
 	int _slen;
 	int _trans;
@@ -144,7 +144,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 148 "../../src/hb-ot-shape-complex-sea-machine.hh"
+#line 148 "hb-ot-shape-complex-sea-machine.hh"
 	}
 
 	_keys = _sea_syllable_machine_trans_keys + (cs<<1);
@@ -167,30 +167,30 @@
 	{te = p+1;}
 	break;
 	case 6:
-#line 63 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 63 "hb-ot-shape-complex-sea-machine.rl"
 	{te = p+1;{ found_syllable (non_sea_cluster); }}
 	break;
 	case 7:
-#line 61 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 61 "hb-ot-shape-complex-sea-machine.rl"
 	{te = p;p--;{ found_syllable (consonant_syllable); }}
 	break;
 	case 8:
-#line 62 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 62 "hb-ot-shape-complex-sea-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 9:
-#line 63 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 63 "hb-ot-shape-complex-sea-machine.rl"
 	{te = p;p--;{ found_syllable (non_sea_cluster); }}
 	break;
 	case 1:
-#line 61 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 61 "hb-ot-shape-complex-sea-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
 	break;
 	case 3:
-#line 62 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 62 "hb-ot-shape-complex-sea-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
 	break;
-#line 194 "../../src/hb-ot-shape-complex-sea-machine.hh"
+#line 194 "hb-ot-shape-complex-sea-machine.hh"
 	}
 
 _again:
@@ -199,7 +199,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 203 "../../src/hb-ot-shape-complex-sea-machine.hh"
+#line 203 "hb-ot-shape-complex-sea-machine.hh"
 	}
 
 	if ( ++p != pe )
@@ -215,7 +215,7 @@
 
 	}
 
-#line 97 "../../src/hb-ot-shape-complex-sea-machine.rl"
+#line 97 "hb-ot-shape-complex-sea-machine.rl"
 
 }
 
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-fallback.cc b/third_party/harfbuzz-ng/src/hb-ot-shape-fallback.cc
index 80d7da8..53274b5 100644
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-fallback.cc
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-fallback.cc
@@ -441,13 +441,15 @@
   OT::hb_apply_context_t c (1, font, buffer);
   c.set_lookup_mask (plan->kern_mask);
   c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
+  OT::hb_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input;
+  skippy_iter.init (&c);
 
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   hb_glyph_position_t *pos = buffer->pos;
   for (unsigned int idx = 0; idx < count;)
   {
-    OT::hb_apply_context_t::skipping_forward_iterator_t skippy_iter (&c, idx, 1);
+    skippy_iter.reset (idx, 1);
     if (!skippy_iter.next ())
     {
       idx++;
diff --git a/third_party/harfbuzz-ng/src/hb-private.hh b/third_party/harfbuzz-ng/src/hb-private.hh
index 45b7712..06b24a8 100644
--- a/third_party/harfbuzz-ng/src/hb-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-private.hh
@@ -94,16 +94,6 @@
 # endif
 #endif
 
-#ifdef _MSC_VER
-#undef inline
-#define inline __inline
-#endif
-
-#ifdef __STRICT_ANSI__
-#undef inline
-#define inline __inline__
-#endif
-
 #if __GNUC__ >= 3
 #define HB_FUNC __PRETTY_FUNCTION__
 #elif defined(_MSC_VER)
@@ -583,6 +573,30 @@
 #define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
 #define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0))
 
+static inline void
+_hb_print_func (const char *func)
+{
+  if (func)
+  {
+    unsigned int func_len = strlen (func);
+    /* Skip "static" */
+    if (0 == strncmp (func, "static ", 7))
+      func += 7;
+    /* Skip "typename" */
+    if (0 == strncmp (func, "typename ", 9))
+      func += 9;
+    /* Skip return type */
+    const char *space = strchr (func, ' ');
+    if (space)
+      func = space + 1;
+    /* Skip parameter list */
+    const char *paren = strchr (func, '(');
+    if (paren)
+      func_len = paren - func;
+    fprintf (stderr, "%.*s", func_len, func);
+  }
+}
+
 template <int max_level> static inline void
 _hb_debug_msg_va (const char *what,
 		  const void *obj,
@@ -628,27 +642,13 @@
   } else
     fprintf (stderr, "   " VRBAR LBAR);
 
-  if (func)
-  {
-    unsigned int func_len = strlen (func);
-#ifndef HB_DEBUG_VERBOSE
-    /* Skip "typename" */
-    if (0 == strncmp (func, "typename ", 9))
-      func += 9;
-    /* Skip return type */
-    const char *space = strchr (func, ' ');
-    if (space)
-      func = space + 1;
-    /* Skip parameter list */
-    const char *paren = strchr (func, '(');
-    if (paren)
-      func_len = paren - func;
-#endif
-    fprintf (stderr, "%.*s: ", func_len, func);
-  }
+  _hb_print_func (func);
 
   if (message)
+  {
+    fprintf (stderr, ": ");
     vfprintf (stderr, message, ap);
+  }
 
   fprintf (stderr, "\n");
 }
@@ -820,7 +820,7 @@
   /* The sizeof() is here to force template instantiation.
    * I'm sure there are better ways to do this but can't think of
    * one right now.  Declaring a variable won't work as HB_UNUSED
-   * is unsable on some platforms and unused types are less likely
+   * is unusable on some platforms and unused types are less likely
    * to generate a warning than unused variables. */
   ASSERT_STATIC (sizeof (hb_assert_unsigned_t<T>) >= 0);
 
diff --git a/third_party/harfbuzz-ng/src/hb-set-private.hh b/third_party/harfbuzz-ng/src/hb-set-private.hh
index 59e8f45..acba4e9 100644
--- a/third_party/harfbuzz-ng/src/hb-set-private.hh
+++ b/third_party/harfbuzz-ng/src/hb-set-private.hh
@@ -145,6 +145,8 @@
 
 struct hb_set_t
 {
+  friend struct hb_frozen_set_t;
+
   hb_object_header_t header;
   ASSERT_POD ();
   bool in_error;
@@ -326,7 +328,7 @@
   static  const hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
 
   elt_t &elt (hb_codepoint_t g) { return elts[g >> SHIFT]; }
-  elt_t elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
+  elt_t const &elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
   elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
 
   elt_t elts[ELTS]; /* XXX 8kb */
@@ -335,6 +337,59 @@
   ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G);
 };
 
+struct hb_frozen_set_t
+{
+  static const unsigned int SHIFT = hb_set_t::SHIFT;
+  static const unsigned int BITS = hb_set_t::BITS;
+  static const unsigned int MASK = hb_set_t::MASK;
+  typedef hb_set_t::elt_t elt_t;
+
+  inline void init (const hb_set_t &set)
+  {
+    start = count = 0;
+    elts = NULL;
+
+    unsigned int max = set.get_max ();
+    if (max == set.INVALID)
+      return;
+    unsigned int min = set.get_min ();
+    const elt_t &min_elt = set.elt (min);
+    const elt_t &max_elt = set.elt (max);
+
+    start = min & ~MASK;
+    count = max - start + 1;
+    unsigned int num_elts = (count + BITS - 1) / BITS;
+    unsigned int elts_size = num_elts * sizeof (elt_t);
+    elts = (elt_t *) malloc (elts_size);
+    if (unlikely (!elts))
+    {
+      start = count = 0;
+      return;
+    }
+    memcpy (elts, &min_elt, elts_size);
+  }
+
+  inline void fini (void)
+  {
+    if (elts)
+      free (elts);
+  }
+
+  inline bool has (hb_codepoint_t g) const
+  {
+    /* hb_codepoint_t is unsigned. */
+    g -= start;
+    if (unlikely (g > count)) return false;
+    return !!(elt (g) & mask (g));
+  }
+
+  elt_t const &elt (hb_codepoint_t g) const { return elts[g >> SHIFT]; }
+  elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
+
+  private:
+  hb_codepoint_t start, count;
+  elt_t *elts;
+};
 
 
 #endif /* HB_SET_PRIVATE_HH */
diff --git a/third_party/harfbuzz-ng/src/hb-version.h b/third_party/harfbuzz-ng/src/hb-version.h
index 896b065..648a46f 100644
--- a/third_party/harfbuzz-ng/src/hb-version.h
+++ b/third_party/harfbuzz-ng/src/hb-version.h
@@ -38,9 +38,9 @@
 
 #define HB_VERSION_MAJOR 0
 #define HB_VERSION_MINOR 9
-#define HB_VERSION_MICRO 37
+#define HB_VERSION_MICRO 40
 
-#define HB_VERSION_STRING "0.9.37"
+#define HB_VERSION_STRING "0.9.40"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
diff --git a/third_party/ots/README.chromium b/third_party/ots/README.chromium
index 906fd3b..ffe584d 100644
--- a/third_party/ots/README.chromium
+++ b/third_party/ots/README.chromium
@@ -1,6 +1,6 @@
 Name: OTS
 URL: https://github.com/khaledhosny/ots.git
-Version: ea88f974e00e7fe0b4fbfe8d0adad8cfedf49c57
+Version: 4d011721c2eadd4e649326f8d164423db060d85e
 Security Critical: yes
 License: BSD
 
diff --git a/third_party/ots/include/opentype-sanitiser.h b/third_party/ots/include/opentype-sanitiser.h
index c454f1e..1c445ae 100644
--- a/third_party/ots/include/opentype-sanitiser.h
+++ b/third_party/ots/include/opentype-sanitiser.h
@@ -203,7 +203,6 @@
     //     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.
@@ -222,10 +221,6 @@
 // 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/src/cmap.cc b/third_party/ots/src/cmap.cc
index d9ca9fa..b1bf5fd 100644
--- a/third_party/ots/src/cmap.cc
+++ b/third_party/ots/src/cmap.cc
@@ -684,11 +684,11 @@
         + (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));
+      OTS_WARNING("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;
   }
diff --git a/third_party/ots/src/ots.cc b/third_party/ots/src/ots.cc
index 197c649..5ba8dd9 100644
--- a/third_party/ots/src/ots.cc
+++ b/third_party/ots/src/ots.cc
@@ -284,7 +284,7 @@
   // 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");
+    return OTS_FAILURE_MSG_HDR("Failed to read 'majorVersion' or 'minorVersion'");
   }
 
   // Checks metadata block size.
@@ -294,11 +294,11 @@
   if (!file.ReadU32(&meta_offset) ||
       !file.ReadU32(&meta_length) ||
       !file.ReadU32(&meta_length_orig)) {
-    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+    return OTS_FAILURE_MSG_HDR("Failed to read header metadata block fields");
   }
   if (meta_offset) {
     if (meta_offset >= length || length - meta_offset < meta_length) {
-      return OTS_FAILURE_MSG_HDR("invalid metadata block location/size");
+      return OTS_FAILURE_MSG_HDR("Invalid metadata block offset or length");
     }
   }
 
@@ -307,11 +307,11 @@
   uint32_t priv_length;
   if (!file.ReadU32(&priv_offset) ||
       !file.ReadU32(&priv_length)) {
-    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+    return OTS_FAILURE_MSG_HDR("Failed to read header private block fields");
   }
   if (priv_offset) {
     if (priv_offset >= length || length - priv_offset < priv_length) {
-      return OTS_FAILURE_MSG_HDR("invalid private block location/size");
+      return OTS_FAILURE_MSG_HDR("Invalid private block offset or length");
     }
   }
 
@@ -366,26 +366,26 @@
   }
   if (meta_offset) {
     if (block_end != meta_offset) {
-      return OTS_FAILURE_MSG_HDR("invalid metadata block location");
+      return OTS_FAILURE_MSG_HDR("Invalid metadata block offset");
     }
     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");
+      return OTS_FAILURE_MSG_HDR("Invalid metadata block length");
     }
   }
   if (priv_offset) {
     if (block_end != priv_offset) {
-      return OTS_FAILURE_MSG_HDR("invalid private block location");
+      return OTS_FAILURE_MSG_HDR("Invalid private block offset");
     }
     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");
+      return OTS_FAILURE_MSG_HDR("Invalid private block length");
     }
   }
   if (block_end != ots::Round4(length)) {
-    return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
+    return OTS_FAILURE_MSG_HDR("File length mismatch (trailing junk?)");
   }
 
   return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
@@ -395,17 +395,17 @@
                   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();
+    return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0");
   }
   // decompressed font must be <= 30MB
   if (decompressed_size > 30 * 1024 * 1024) {
-    return OTS_FAILURE();
+    return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds 30MB");
   }
 
   std::vector<uint8_t> decompressed_buffer(decompressed_size);
-  if (!ots::ConvertWOFF2ToTTF(header, &decompressed_buffer[0], decompressed_size,
-                              data, length)) {
-    return OTS_FAILURE();
+  if (!ots::ConvertWOFF2ToSFNT(header, &decompressed_buffer[0], decompressed_size,
+                               data, length)) {
+    return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT");
   }
   return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
 }
@@ -599,8 +599,14 @@
   } 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");
+#define PASSTHRU_TABLE(TAG) (table_map.find(Tag(TAG)) != table_map.end() && \
+                             GetTableAction(header, Tag(TAG)) == ots::TABLE_ACTION_PASSTHRU)
+      // We don't sanitise bitmap table, but don't reject bitmap-only fonts if
+      // we keep the tables.
+      if (!PASSTHRU_TABLE("CBDT") || !PASSTHRU_TABLE("CBLC")) {
+        return OTS_FAILURE_MSG_HDR("no supported glyph shapes table(s) present");
+      }
+#undef PASSTHRU_TABLE
     }
   }
 
@@ -776,9 +782,6 @@
 
 namespace ots {
 
-void EnableWOFF2() {
-}
-
 bool IsValidVersionTag(uint32_t tag) {
   return tag == Tag("\x00\x01\x00\x00") ||
          // OpenType fonts with CFF data have 'OTTO' tag.
diff --git a/third_party/ots/src/woff2.cc b/third_party/ots/src/woff2.cc
index 54ab625..b244aec 100644
--- a/third_party/ots/src/woff2.cc
+++ b/third_party/ots/src/woff2.cc
@@ -45,6 +45,7 @@
 
 // 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)
+#define CHR(t)           (t >> 24),  (t >> 16),  (t >> 8), (t >> 0)
 
 const unsigned int kWoff2FlagsTransform = 1 << 5;
 
@@ -135,6 +136,10 @@
         transform_length(0),
         dst_offset(0),
         dst_length(0) {}
+
+  bool operator<(const Table& other) const {
+    return tag < other.tag;
+  }
 };
 
 // Based on section 6.1.1 of MicroType Express draft spec
@@ -509,35 +514,38 @@
 }
 
 // Reconstruct entire glyf table based on transformed original
-bool ReconstructGlyf(const uint8_t* data, size_t data_size,
+bool ReconstructGlyf(ots::OpenTypeFile* file,
+    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);
+  ots::Buffer buffer(data, data_size);
   uint32_t version;
   std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
 
-  if (!file.ReadU32(&version)) {
-    return OTS_FAILURE();
+  if (!buffer.ReadU32(&version)) {
+    return OTS_FAILURE_MSG("Failed to read 'version' of transformed 'glyf' table");
   }
   uint16_t num_glyphs;
+  if (!buffer.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read 'numGlyphs' from transformed 'glyf' table");
+  }
   uint16_t index_format;
-  if (!file.ReadU16(&num_glyphs) ||
-      !file.ReadU16(&index_format)) {
-    return OTS_FAILURE();
+  if (!buffer.ReadU16(&index_format)) {
+    return OTS_FAILURE_MSG("Failed to read 'indexFormat' from transformed 'glyf' table");
   }
   unsigned int offset = (2 + kNumSubStreams) * 4;
   if (offset > data_size) {
-    return OTS_FAILURE();
+    return OTS_FAILURE_MSG("Size of transformed 'glyf' table is too small to fit its data");
   }
   // 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 (!buffer.ReadU32(&substream_size)) {
+      return OTS_FAILURE_MSG("Failed to read substream size %d of transformed 'glyf' table", i);
     }
     if (substream_size > data_size - offset) {
-      return OTS_FAILURE();
+      return OTS_FAILURE_MSG("Size of substream %d of transformed 'glyf' table does not fit in table size");
     }
     substreams.at(i) = std::make_pair(data + offset, substream_size);
     offset += substream_size;
@@ -560,7 +568,7 @@
     size_t glyph_size = 0;
     uint16_t n_contours = 0;
     if (!n_contour_stream.ReadU16(&n_contours)) {
-      return OTS_FAILURE();
+      return OTS_FAILURE_MSG("Filed to read 'numberOfContours' of glyph %d from transformed 'glyf' table", i);
     }
     uint8_t* glyf_dst = dst + loca_offset;
     size_t glyf_dst_size = dst_size - loca_offset;
@@ -570,20 +578,20 @@
       uint16_t instruction_size = 0;
       if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
             &glyph_size, &have_instructions)) {
-        return OTS_FAILURE();
+        return OTS_FAILURE_MSG("Filed to process composite glyph %d from transformed 'glyf' table", i);
       }
       if (have_instructions) {
         if (!Read255UShort(&glyph_stream, &instruction_size)) {
-          return OTS_FAILURE();
+          return OTS_FAILURE_MSG("Failed to read 'instructionLength' of glyph %d from transformed 'glyf' table", i);
         }
         // No integer overflow here (instruction_size < 2^16).
         if (instruction_size + 2U > glyf_dst_size - glyph_size) {
-          return OTS_FAILURE();
+          return OTS_FAILURE_MSG("'instructionLength' of glyph %d from transformed 'glyf' table does not fit in the destination glyph size", i);
         }
         StoreU16(glyf_dst, glyph_size, instruction_size);
         if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
               instruction_size)) {
-          return OTS_FAILURE();
+          return OTS_FAILURE_MSG("Filed to read instructions of glyph %d from transformed 'glyf' table", i);
         }
         glyph_size += instruction_size + 2;
       }
@@ -595,11 +603,11 @@
       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();
+          return OTS_FAILURE_MSG("Filed to read number of points of contour %d of glyph %d from transformed 'glyf' table", j, i);
         }
         n_points_vec.push_back(n_points_contour);
         if (total_n_points + n_points_contour < total_n_points) {
-          return OTS_FAILURE();
+          return OTS_FAILURE_MSG("Negative number of points of contour %d of glyph %d from transformed 'glyf' table", j, i);
         }
         total_n_points += n_points_contour;
       }
@@ -654,7 +662,7 @@
       }
       if (!StorePoints(points, n_contours, instruction_size,
             glyf_dst, glyf_dst_size, &glyph_size)) {
-        return OTS_FAILURE();
+        return OTS_FAILURE_MSG("Failed to store points of glyph %d from the transformed 'glyf' table", i);
       }
     } else {
       glyph_size = 0;
@@ -675,7 +683,7 @@
   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 OTS_FAILURE_MSG("Filed to process 'bboxStream' from the transformed 'glyf' table");
   }
   return StoreLoca(loca_values, index_format, loca_buf, loca_size);
 }
@@ -693,7 +701,8 @@
   return NULL;
 }
 
-bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
+bool ReconstructTransformed(ots::OpenTypeFile* file,
+    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')) {
@@ -710,7 +719,7 @@
         dst_length) {
       return OTS_FAILURE();
     }
-    return ReconstructGlyf(transformed_buf, transformed_size,
+    return ReconstructGlyf(file, 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')) {
@@ -803,18 +812,18 @@
     }
     uint32_t dst_length;
     if (!ReadBase128(buffer, &dst_length)) {
-      return OTS_FAILURE_MSG("Failed to read \"origLength\" for table %4.4s", (char*)&tag);
+      return OTS_FAILURE_MSG("Failed to read 'origLength' for table '%c%c%c%c'", CHR(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);
+        return OTS_FAILURE_MSG("Failed to read 'transformLength' for table '%c%c%c%c'", CHR(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");
+      return OTS_FAILURE_MSG("'origLength' or 'transformLength' > 1GB");
     }
     table->tag = tag;
     table->flags = flags;
@@ -839,9 +848,9 @@
   return total_length;
 }
 
-bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
-                       uint8_t* result, size_t result_length,
-                       const uint8_t* data, size_t length) {
+bool ConvertWOFF2ToSFNT(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);
 
@@ -849,30 +858,35 @@
   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");
+    return OTS_FAILURE_MSG("Failed to read 'signature' or 'flavor', or not WOFF2 signature");
   }
 
   if (!IsValidVersionTag(ntohl(flavor))) {
-    return OTS_FAILURE_MSG("Invalid \"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");
+    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\"");
+    return OTS_FAILURE_MSG("Failed to read 'numTables'");
   }
+
+  uint16_t reserved_value;
+  if (!buffer.ReadU16(&reserved_value)) {
+    return OTS_FAILURE_MSG("Failed to read 'reserved' field");
+  }
+
   // 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 total_sfnt_size, the caller already passes it as result_length
+  if (!buffer.Skip(4)) {
+    return OTS_FAILURE_MSG("Failed to read 'totalSfntSize'");
   }
   uint32_t compressed_length;
   if (!buffer.ReadU32(&compressed_length)) {
-    return OTS_FAILURE_MSG("Failed to read \"totalCompressedSize\"");
+    return OTS_FAILURE_MSG("Failed to read 'totalCompressedSize'");
   }
   if (compressed_length > std::numeric_limits<uint32_t>::max()) {
     return OTS_FAILURE();
@@ -880,11 +894,38 @@
 
   // 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();
+  if (!buffer.Skip(2 * 2)) {
+    return OTS_FAILURE_MSG("Failed to read 'majorVersion' or 'minorVersion'");
   }
+
+  // Checks metadata block size.
+  uint32_t meta_offset;
+  uint32_t meta_length;
+  uint32_t meta_length_orig;
+  if (!buffer.ReadU32(&meta_offset) ||
+      !buffer.ReadU32(&meta_length) ||
+      !buffer.ReadU32(&meta_length_orig)) {
+    return OTS_FAILURE_MSG("Failed to read header metadata block fields");
+  }
+  if (meta_offset) {
+    if (meta_offset >= length || length - meta_offset < meta_length) {
+      return OTS_FAILURE_MSG("Invalid metadata block offset or length");
+    }
+  }
+
+  // Checks private data block size.
+  uint32_t priv_offset;
+  uint32_t priv_length;
+  if (!buffer.ReadU32(&priv_offset) ||
+      !buffer.ReadU32(&priv_length)) {
+    return OTS_FAILURE_MSG("Failed to read header private block fields");
+  }
+  if (priv_offset) {
+    if (priv_offset >= length || length - priv_offset < priv_length) {
+      return OTS_FAILURE_MSG("Invalid private block offset or length");
+    }
+  }
+
   std::vector<Table> tables(num_tables);
   if (!ReadTableDirectory(file, &buffer, &tables, num_tables)) {
     return OTS_FAILURE_MSG("Failed to read table directory");
@@ -904,8 +945,10 @@
     }
     dst_offset = ots::Round4(dst_offset);
   }
-  if (ots::Round4(compressed_offset + compressed_length) > length || dst_offset > result_length) {
-    return OTS_FAILURE();
+
+  uint64_t block_end = ots::Round4(compressed_offset + compressed_length);
+  if (block_end > length || dst_offset != result_length) {
+    return OTS_FAILURE_MSG("Uncompressed sfnt size mismatch");
   }
 
   const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
@@ -913,6 +956,32 @@
     return OTS_FAILURE();
   }
 
+  if (meta_offset) {
+    if (block_end != meta_offset) {
+      return OTS_FAILURE_MSG("Invalid metadata block offset");
+    }
+    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("Invalid metadata block length");
+    }
+  }
+
+  if (priv_offset) {
+    if (block_end != priv_offset) {
+      return OTS_FAILURE_MSG("Invalid private block offset");
+    }
+    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("Invalid private block length");
+    }
+  }
+
+  if (block_end != ots::Round4(length)) {
+    return OTS_FAILURE_MSG("File length mismatch (trailing junk?)");
+  }
+
   // Start building the font
   size_t offset = 0;
   offset = StoreU32(result, offset, flavor);
@@ -925,8 +994,13 @@
   offset = StoreU16(result, offset, output_search_range);
   offset = StoreU16(result, offset, max_pow2);
   offset = StoreU16(result, offset, (num_tables << 4) - output_search_range);
+
+  // sort tags in the table directory in ascending alphabetical order
+  std::vector<Table> sorted_tables(tables);
+  std::sort(sorted_tables.begin(), sorted_tables.end());
+
   for (uint16_t i = 0; i < num_tables; ++i) {
-    const Table* table = &tables.at(i);
+    const Table* table = &sorted_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);
@@ -951,7 +1025,7 @@
   const uint8_t* src_buf = data + compressed_offset;
   if (!Woff2Uncompress(&uncompressed_buf[0], total_size_size_t,
       src_buf, compressed_length)) {
-    return OTS_FAILURE();
+    return OTS_FAILURE_MSG("Failed to uncompress font data");
   }
   transform_buf = &uncompressed_buf[0];
 
@@ -971,9 +1045,9 @@
       std::memcpy(result + table->dst_offset, transform_buf,
           transform_length);
     } else {
-      if (!ReconstructTransformed(tables, table->tag,
+      if (!ReconstructTransformed(file, tables, table->tag,
             transform_buf, transform_length, result, result_length)) {
-        return OTS_FAILURE();
+        return OTS_FAILURE_MSG("Failed to reconstruct '%c%c%c%c' table", CHR(table->tag));
       }
     }
 
@@ -983,7 +1057,7 @@
     }
   }
 
-  return FixChecksums(tables, result);
+  return FixChecksums(sorted_tables, result);
 }
 
 }  // namespace ots
diff --git a/third_party/ots/src/woff2.h b/third_party/ots/src/woff2.h
index 1db259a..f03f4f5 100644
--- a/third_party/ots/src/woff2.h
+++ b/third_party/ots/src/woff2.h
@@ -13,8 +13,8 @@
 // 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);
+bool ConvertWOFF2ToSFNT(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/protobuf/protobuf.gyp b/third_party/protobuf/protobuf.gyp
index 25ddbae..7417eec 100644
--- a/third_party/protobuf/protobuf.gyp
+++ b/third_party/protobuf/protobuf.gyp
@@ -29,7 +29,7 @@
             ],
           },
         }],
-        ['OS=="ios" and "<(GENERATOR)"!="ninja"', {
+        ['OS=="ios" and "<(GENERATOR)"=="xcode" and "<(GENERATOR_FLAVOR)"!="ninja"', {
           'variables': {
             'ninja_output_dir': 'ninja-protoc',
             'ninja_product_dir':
@@ -170,7 +170,7 @@
         {
           'target_name': 'protoc',
           'conditions': [
-            ['OS!="ios" or "<(GENERATOR)"=="ninja"', {
+            ['OS!="ios" or "<(GENERATOR)"!="xcode" or "<(GENERATOR_FLAVOR)"=="ninja"', {
               'type': 'executable',
               'toolsets': ['host'],
               'sources': [
@@ -238,7 +238,7 @@
                 '<(config_h_dir)',
                 'src/src',
               ],
-            }, {  # else, OS=="ios" and "<(GENERATOR)"!="ninja"
+            }, {  # else, OS=="ios" and "<(GENERATOR)"=="xcode" and "<(GENERATOR_FLAVOR)"!="ninja"
               'type': 'none',
               'toolsets': ['host'],
               'dependencies': [
diff --git a/third_party/qcms/README.chromium b/third_party/qcms/README.chromium
index fc027b0..fdd921d 100644
--- a/third_party/qcms/README.chromium
+++ b/third_party/qcms/README.chromium
@@ -57,5 +57,7 @@
    - https://code.google.com/p/chromium/issues/detail?id=443863
  - Apply upstream fix reject invalid sizes from
    - https://bugzilla.mozilla.org/show_bug.cgi?id=1132468
+ - lut_inverse_interp16: remove odd whitespace formatting
+   - https://code.google.com/p/chromium/issues/detail?id=458024
 To regenerate google.patch:
   git diff b8456f38 src > google.patch
diff --git a/third_party/qcms/google.patch b/third_party/qcms/google.patch
index dea54fc..cf8b7c2 100644
--- a/third_party/qcms/google.patch
+++ b/third_party/qcms/google.patch
@@ -1,5 +1,5 @@
 diff --git a/third_party/qcms/src/iccread.c b/third_party/qcms/src/iccread.c
-index 36b7011..6cec34a 100644
+index 36b7011..208ebee 100644
 --- a/third_party/qcms/src/iccread.c
 +++ b/third_party/qcms/src/iccread.c
 @@ -266,7 +266,7 @@ qcms_bool qcms_profile_is_bogus(qcms_profile *profile)
@@ -230,7 +230,36 @@
  }
  
  static void mAB_release(struct lutmABType *lut)
-@@ -657,7 +817,7 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
+@@ -540,7 +700,7 @@ static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag
+ 	// We require 3in/out channels since we only support RGB->XYZ (or RGB->LAB)
+ 	// XXX: If we remove this restriction make sure that the number of channels
+ 	//      is less or equal to the maximum number of mAB curves in qcmsint.h
+-	//      also check for clut_size overflow.
++	//      also check for clut_size overflow. Also make sure it's != 0
+ 	if (num_in_channels != 3 || num_out_channels != 3)
+ 		return NULL;
+ 
+@@ -570,6 +730,9 @@ static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag
+ 		// clut_size can not overflow since lg(256^num_in_channels) = 24 bits.
+ 		for (i = 0; i < num_in_channels; i++) {
+ 			clut_size *= read_u8(src, clut_offset + i);
++			if (clut_size == 0) {
++				invalid_source(src, "bad clut_size");
++			}
+ 		}
+ 	} else {
+ 		clut_size = 0;
+@@ -590,6 +753,9 @@ static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag
+ 
+ 	for (i = 0; i < num_in_channels; i++) {
+ 		lut->num_grid_points[i] = read_u8(src, clut_offset + i);
++		if (lut->num_grid_points[i] == 0) {
++			invalid_source(src, "bad grid_points");
++		}
+ 	}
+ 
+ 	// Reverse the processing of transformation elements for mBA type.
+@@ -657,7 +823,7 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
  	uint16_t num_input_table_entries;
  	uint16_t num_output_table_entries;
  	uint8_t in_chan, grid_points, out_chan;
@@ -239,7 +268,50 @@
  	uint32_t clut_size;
  	size_t entry_size;
  	struct lutType *lut;
-@@ -979,6 +1139,9 @@ qcms_profile* qcms_profile_sRGB(void)
+@@ -672,6 +838,10 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
+ 	} else if (type == LUT16_TYPE) {
+ 		num_input_table_entries  = read_u16(src, offset + 48);
+ 		num_output_table_entries = read_u16(src, offset + 50);
++		if (num_input_table_entries == 0 || num_output_table_entries == 0) {
++			invalid_source(src, "Bad channel count");
++			return NULL;
++		}
+ 		entry_size = 2;
+ 	} else {
+ 		assert(0); // the caller checks that this doesn't happen
+@@ -685,15 +855,18 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
+ 
+ 	clut_size = pow(grid_points, in_chan);
+ 	if (clut_size > MAX_CLUT_SIZE) {
++		invalid_source(src, "CLUT too large");
+ 		return NULL;
+ 	}
+ 
+ 	if (in_chan != 3 || out_chan != 3) {
++		invalid_source(src, "CLUT only supports RGB");
+ 		return NULL;
+ 	}
+ 
+ 	lut = malloc(sizeof(struct lutType) + (num_input_table_entries * in_chan + clut_size*out_chan + num_output_table_entries * out_chan)*sizeof(float));
+ 	if (!lut) {
++		invalid_source(src, "CLUT too large");
+ 		return NULL;
+ 	}
+ 
+@@ -704,9 +877,9 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
+ 
+ 	lut->num_input_table_entries  = num_input_table_entries;
+ 	lut->num_output_table_entries = num_output_table_entries;
+-	lut->num_input_channels   = read_u8(src, offset + 8);
+-	lut->num_output_channels  = read_u8(src, offset + 9);
+-	lut->num_clut_grid_points = read_u8(src, offset + 10);
++	lut->num_input_channels   = in_chan;
++	lut->num_output_channels  = out_chan;
++	lut->num_clut_grid_points = grid_points;
+ 	lut->e00 = read_s15Fixed16Number(src, offset+12);
+ 	lut->e01 = read_s15Fixed16Number(src, offset+16);
+ 	lut->e02 = read_s15Fixed16Number(src, offset+20);
+@@ -979,6 +1152,9 @@ qcms_profile* qcms_profile_sRGB(void)
  		return NO_MEM_PROFILE;
  
  	profile = qcms_profile_create_rgb_with_table(D65, Rec709Primaries, table, 1024);
@@ -249,7 +321,7 @@
  	free(table);
  	return profile;
  }
-@@ -997,6 +1160,9 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
+@@ -997,6 +1173,9 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
  	source.size = size;
  	source.valid = true;
  
@@ -259,7 +331,7 @@
  	length = read_u32(src, 0);
  	if (length <= size) {
  		// shrink the area that we can read if appropriate
-@@ -1028,6 +1194,15 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
+@@ -1028,6 +1207,15 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
  	if (!src->valid || !index.tags)
  		goto invalid_tag_table;
  
@@ -275,7 +347,7 @@
  	if (find_tag(index, TAG_CHAD)) {
  		profile->chromaticAdaption = read_tag_s15Fixed16ArrayType(src, index, TAG_CHAD);
  	} else {
-@@ -1098,6 +1273,16 @@ invalid_profile:
+@@ -1098,6 +1286,16 @@ invalid_profile:
  	return INVALID_PROFILE;
  }
  
@@ -1282,7 +1354,7 @@
  
  qcms_bool qcms_supports_iccv4;
 diff --git a/third_party/qcms/src/transform_util.c b/third_party/qcms/src/transform_util.c
-index e8447e5..f4338b2 100644
+index e8447e5..f616c3f 100644
 --- a/third_party/qcms/src/transform_util.c
 +++ b/third_party/qcms/src/transform_util.c
 @@ -36,7 +36,7 @@
@@ -1365,7 +1437,88 @@
  }
  
  struct matrix build_colorant_matrix(qcms_profile *p)
-@@ -390,7 +408,7 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
+@@ -295,7 +313,7 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
+ 
+         NumZeroes = 0;
+         while (LutTable[NumZeroes] == 0 && NumZeroes < length-1)
+-                        NumZeroes++;
++            NumZeroes++;
+ 
+         // There are no zeros at the beginning and we are trying to find a zero, so
+         // return anything. It seems zero would be the less destructive choice
+@@ -305,22 +323,22 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
+ 
+         NumPoles = 0;
+         while (LutTable[length-1- NumPoles] == 0xFFFF && NumPoles < length-1)
+-                        NumPoles++;
++            NumPoles++;
+ 
+         // Does the curve belong to this case?
+         if (NumZeroes > 1 || NumPoles > 1)
+-        {               
++        {
+                 int a, b;
+ 
+-                // Identify if value fall downto 0 or FFFF zone             
++                // Identify if value fall downto 0 or FFFF zone
+                 if (Value == 0) return 0;
+                // if (Value == 0xFFFF) return 0xFFFF;
+ 
+                 // else restrict to valid zone
+ 
+-                a = ((NumZeroes-1) * 0xFFFF) / (length-1);               
++                a = ((NumZeroes-1) * 0xFFFF) / (length-1);
+                 b = ((length-1 - NumPoles) * 0xFFFF) / (length-1);
+-                                                                
++
+                 l = a - 1;
+                 r = b + 1;
+         }
+@@ -332,12 +350,12 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
+ 
+                 x = (l + r) / 2;
+ 
+-		res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
++                res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
+ 
+                 if (res == Value) {
+ 
+-                    // Found exact match. 
+-                    
++                    // Found exact match.
++
+                     return (uint16_fract_t) (x - 1);
+                 }
+ 
+@@ -347,14 +365,14 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
+ 
+         // Not found, should we interpolate?
+ 
+-                
++
+         // Get surrounding nodes
+-        
++
+         val2 = (length-1) * ((double) (x - 1) / 65535.0);
+ 
+         cell0 = (int) floor(val2);
+         cell1 = (int) ceil(val2);
+-           
++
+         if (cell0 == cell1) return (uint16_fract_t) x;
+ 
+         y0 = LutTable[cell0] ;
+@@ -373,8 +391,7 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
+         if (f < 0.0) return (uint16_fract_t) 0;
+         if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
+ 
+-        return (uint16_fract_t) floor(f + 0.5);                        
+-
++        return (uint16_fract_t) floor(f + 0.5);
+ }
+ 
+ /*
+@@ -390,7 +407,7 @@ uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len
   which has an maximum error of about 9855 (pixel difference of ~38.346)
  
   For now, we punt the decision of output size to the caller. */
diff --git a/third_party/qcms/src/transform_util.c b/third_party/qcms/src/transform_util.c
index f4338b2..f616c3f 100644
--- a/third_party/qcms/src/transform_util.c
+++ b/third_party/qcms/src/transform_util.c
@@ -313,7 +313,7 @@
 
         NumZeroes = 0;
         while (LutTable[NumZeroes] == 0 && NumZeroes < length-1)
-                        NumZeroes++;
+            NumZeroes++;
 
         // There are no zeros at the beginning and we are trying to find a zero, so
         // return anything. It seems zero would be the less destructive choice
@@ -323,22 +323,22 @@
 
         NumPoles = 0;
         while (LutTable[length-1- NumPoles] == 0xFFFF && NumPoles < length-1)
-                        NumPoles++;
+            NumPoles++;
 
         // Does the curve belong to this case?
         if (NumZeroes > 1 || NumPoles > 1)
-        {               
+        {
                 int a, b;
 
-                // Identify if value fall downto 0 or FFFF zone             
+                // Identify if value fall downto 0 or FFFF zone
                 if (Value == 0) return 0;
                // if (Value == 0xFFFF) return 0xFFFF;
 
                 // else restrict to valid zone
 
-                a = ((NumZeroes-1) * 0xFFFF) / (length-1);               
+                a = ((NumZeroes-1) * 0xFFFF) / (length-1);
                 b = ((length-1 - NumPoles) * 0xFFFF) / (length-1);
-                                                                
+
                 l = a - 1;
                 r = b + 1;
         }
@@ -350,12 +350,12 @@
 
                 x = (l + r) / 2;
 
-		res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
+                res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
 
                 if (res == Value) {
 
-                    // Found exact match. 
-                    
+                    // Found exact match.
+
                     return (uint16_fract_t) (x - 1);
                 }
 
@@ -365,14 +365,14 @@
 
         // Not found, should we interpolate?
 
-                
+
         // Get surrounding nodes
-        
+
         val2 = (length-1) * ((double) (x - 1) / 65535.0);
 
         cell0 = (int) floor(val2);
         cell1 = (int) ceil(val2);
-           
+
         if (cell0 == cell1) return (uint16_fract_t) x;
 
         y0 = LutTable[cell0] ;
@@ -391,8 +391,7 @@
         if (f < 0.0) return (uint16_fract_t) 0;
         if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
 
-        return (uint16_fract_t) floor(f + 0.5);                        
-
+        return (uint16_fract_t) floor(f + 0.5);
 }
 
 /*
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index a1dddfb..59d96da 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -23,6 +23,103 @@
 
 namespace zip {
 
+namespace {
+
+// FilePathWriterDelegate ------------------------------------------------------
+
+// A writer delegate that writes a file at a given path.
+class FilePathWriterDelegate : public WriterDelegate {
+ public:
+  explicit FilePathWriterDelegate(const base::FilePath& output_file_path);
+  ~FilePathWriterDelegate() override;
+
+  // WriterDelegate methods:
+
+  // Creates the output file and any necessary intermediate directories.
+  bool PrepareOutput() override;
+
+  // Writes |num_bytes| bytes of |data| to the file, returning false if not all
+  // bytes could be written.
+  bool WriteBytes(const char* data, int num_bytes) override;
+
+ private:
+  base::FilePath output_file_path_;
+  base::File file_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate);
+};
+
+FilePathWriterDelegate::FilePathWriterDelegate(
+    const base::FilePath& output_file_path)
+    : output_file_path_(output_file_path) {
+}
+
+FilePathWriterDelegate::~FilePathWriterDelegate() {
+}
+
+bool FilePathWriterDelegate::PrepareOutput() {
+  // We can't rely on parent directory entries being specified in the
+  // zip, so we make sure they are created.
+  if (!base::CreateDirectory(output_file_path_.DirName()))
+    return false;
+
+  file_.Initialize(output_file_path_,
+                   base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  return file_.IsValid();
+}
+
+bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
+  return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
+}
+
+
+// StringWriterDelegate --------------------------------------------------------
+
+// A writer delegate that writes no more than |max_read_bytes| to a given
+// std::string.
+class StringWriterDelegate : public WriterDelegate {
+ public:
+  StringWriterDelegate(size_t max_read_bytes, std::string* output);
+  ~StringWriterDelegate() override;
+
+  // WriterDelegate methods:
+
+  // Returns true.
+  bool PrepareOutput() override;
+
+  // Appends |num_bytes| bytes from |data| to the output string. Returns false
+  // if |num_bytes| will cause the string to exceed |max_read_bytes|.
+  bool WriteBytes(const char* data, int num_bytes) override;
+
+ private:
+  size_t max_read_bytes_;
+  std::string* output_;
+
+  DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate);
+};
+
+StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes,
+                                           std::string* output)
+    : max_read_bytes_(max_read_bytes),
+      output_(output) {
+}
+
+StringWriterDelegate::~StringWriterDelegate() {
+}
+
+bool StringWriterDelegate::PrepareOutput() {
+  return true;
+}
+
+bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) {
+  if (output_->size() + num_bytes > max_read_bytes_)
+    return false;
+  output_->append(data, num_bytes);
+  return true;
+}
+
+}  // namespace
+
 // TODO(satorux): The implementation assumes that file names in zip files
 // are encoded in UTF-8. This is true for zip files created by Zip()
 // function in zip.h, but not true for user-supplied random zip files.
@@ -187,33 +284,20 @@
   return OpenCurrentEntryInZip();
 }
 
-bool ZipReader::ExtractCurrentEntryToFilePath(
-    const base::FilePath& output_file_path) {
+bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate) const {
   DCHECK(zip_file_);
 
-  // If this is a directory, just create it and return.
-  if (current_entry_info()->is_directory())
-    return base::CreateDirectory(output_file_path);
-
   const int open_result = unzOpenCurrentFile(zip_file_);
   if (open_result != UNZ_OK)
     return false;
 
-  // We can't rely on parent directory entries being specified in the
-  // zip, so we make sure they are created.
-  base::FilePath output_dir_path = output_file_path.DirName();
-  if (!base::CreateDirectory(output_dir_path))
-    return false;
-
-  base::File file(output_file_path,
-                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  if (!file.IsValid())
+  if (!delegate->PrepareOutput())
     return false;
 
   bool success = true;  // This becomes false when something bad happens.
+  scoped_ptr<char[]> buf(new char[internal::kZipBufSize]);
   while (true) {
-    char buf[internal::kZipBufSize];
-    const int num_bytes_read = unzReadCurrentFile(zip_file_, buf,
+    const int num_bytes_read = unzReadCurrentFile(zip_file_, buf.get(),
                                                   internal::kZipBufSize);
     if (num_bytes_read == 0) {
       // Reached the end of the file.
@@ -223,21 +307,39 @@
       success = false;
       break;
     } else if (num_bytes_read > 0) {
-      // Some data is read. Write it to the output file.
-      if (num_bytes_read != file.WriteAtCurrentPos(buf, num_bytes_read)) {
+      // Some data is read.
+      if (!delegate->WriteBytes(buf.get(), num_bytes_read)) {
         success = false;
         break;
       }
     }
   }
 
-  file.Close();
   unzCloseCurrentFile(zip_file_);
 
-  if (current_entry_info()->last_modified() != base::Time::UnixEpoch())
+  return success;
+}
+
+bool ZipReader::ExtractCurrentEntryToFilePath(
+    const base::FilePath& output_file_path) const {
+  DCHECK(zip_file_);
+
+  // If this is a directory, just create it and return.
+  if (current_entry_info()->is_directory())
+    return base::CreateDirectory(output_file_path);
+
+  bool success = false;
+  {
+    FilePathWriterDelegate writer(output_file_path);
+    success = ExtractCurrentEntry(&writer);
+  }
+
+  if (success &&
+      current_entry_info()->last_modified() != base::Time::UnixEpoch()) {
     base::TouchFile(output_file_path,
                     base::Time::Now(),
                     current_entry_info()->last_modified());
+  }
 
   return success;
 }
@@ -296,7 +398,7 @@
 }
 
 bool ZipReader::ExtractCurrentEntryIntoDirectory(
-    const base::FilePath& output_directory_path) {
+    const base::FilePath& output_directory_path) const {
   DCHECK(current_entry_info_.get());
 
   base::FilePath output_file_path = output_directory_path.Append(
@@ -304,61 +406,29 @@
   return ExtractCurrentEntryToFilePath(output_file_path);
 }
 
-#if defined(OS_POSIX)
-bool ZipReader::ExtractCurrentEntryToFd(const int fd) {
+bool ZipReader::ExtractCurrentEntryToFile(base::File* file) const {
   DCHECK(zip_file_);
 
-  // If this is a directory, there's nothing to extract to the file descriptor,
-  // so return false.
+  // If this is a directory, there's nothing to extract to the file, so return
+  // false.
   if (current_entry_info()->is_directory())
     return false;
 
-  const int open_result = unzOpenCurrentFile(zip_file_);
-  if (open_result != UNZ_OK)
-    return false;
-
-  bool success = true;  // This becomes false when something bad happens.
-  while (true) {
-    char buf[internal::kZipBufSize];
-    const int num_bytes_read = unzReadCurrentFile(zip_file_, buf,
-                                                  internal::kZipBufSize);
-    if (num_bytes_read == 0) {
-      // Reached the end of the file.
-      break;
-    } else if (num_bytes_read < 0) {
-      // If num_bytes_read < 0, then it's a specific UNZ_* error code.
-      success = false;
-      break;
-    } else if (num_bytes_read > 0) {
-      // Some data is read. Write it to the output file descriptor.
-      if (!base::WriteFileDescriptor(fd, buf, num_bytes_read)) {
-        success = false;
-        break;
-      }
-    }
-  }
-
-  unzCloseCurrentFile(zip_file_);
-  return success;
+  FileWriterDelegate writer(file);
+  return ExtractCurrentEntry(&writer);
 }
-#endif  // defined(OS_POSIX)
 
-bool ZipReader::ExtractCurrentEntryToString(
-    size_t max_read_bytes,
-    std::string* output) const {
+bool ZipReader::ExtractCurrentEntryToString(size_t max_read_bytes,
+                                            std::string* output) const {
   DCHECK(output);
   DCHECK(zip_file_);
-  DCHECK(max_read_bytes != 0);
+  DCHECK_NE(0U, max_read_bytes);
 
   if (current_entry_info()->is_directory()) {
     output->clear();
     return true;
   }
 
-  const int open_result = unzOpenCurrentFile(zip_file_);
-  if (open_result != UNZ_OK)
-    return false;
-
   // The original_size() is the best hint for the real size, so it saves
   // doing reallocations for the common case when the uncompressed size is
   // correct. However, we need to assume that the uncompressed size could be
@@ -368,32 +438,11 @@
       static_cast<int64>(max_read_bytes),
       current_entry_info()->original_size())));
 
-  bool success = true;  // This becomes false when something bad happens.
-  char buf[internal::kZipBufSize];
-  while (true) {
-    const int num_bytes_read = unzReadCurrentFile(zip_file_, buf,
-                                                  internal::kZipBufSize);
-    if (num_bytes_read == 0) {
-      // Reached the end of the file.
-      break;
-    } else if (num_bytes_read < 0) {
-      // If num_bytes_read < 0, then it's a specific UNZ_* error code.
-      success = false;
-      break;
-    } else if (num_bytes_read > 0) {
-      if (contents.size() + num_bytes_read > max_read_bytes) {
-        success = false;
-        break;
-      }
-      contents.append(buf, num_bytes_read);
-    }
-  }
-
-  unzCloseCurrentFile(zip_file_);
-  if (success)
-    output->swap(contents);
-
-  return success;
+  StringWriterDelegate writer(max_read_bytes, &contents);
+  if (!ExtractCurrentEntry(&writer))
+    return false;
+  output->swap(contents);
+  return true;
 }
 
 bool ZipReader::OpenInternal() {
@@ -461,5 +510,30 @@
   }
 }
 
+// FileWriterDelegate ----------------------------------------------------------
+
+FileWriterDelegate::FileWriterDelegate(base::File* file)
+    : file_(file),
+      file_length_(0) {
+}
+
+FileWriterDelegate::~FileWriterDelegate() {
+#if !defined(NDEBUG)
+  const bool success =
+#endif
+      file_->SetLength(file_length_);
+  DPLOG_IF(ERROR, !success) << "Failed updating length of written file";
+}
+
+bool FileWriterDelegate::PrepareOutput() {
+  return file_->Seek(base::File::FROM_BEGIN, 0) >= 0;
+}
+
+bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
+  int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
+  if (bytes_written > 0)
+    file_length_ += bytes_written;
+  return bytes_written == num_bytes;
+}
 
 }  // namespace zip
diff --git a/third_party/zlib/google/zip_reader.h b/third_party/zlib/google/zip_reader.h
index 9280f23..da6cc93 100644
--- a/third_party/zlib/google/zip_reader.h
+++ b/third_party/zlib/google/zip_reader.h
@@ -23,6 +23,21 @@
 
 namespace zip {
 
+// A delegate interface used to stream out an entry; see
+// ZipReader::ExtractCurrentEntry.
+class WriterDelegate {
+ public:
+  virtual ~WriterDelegate() {}
+
+  // Invoked once before any data is streamed out to pave the way (e.g., to open
+  // the output file). Return false on failure to cancel extraction.
+  virtual bool PrepareOutput() = 0;
+
+  // Invoked to write the next chunk of data. Return false on failure to cancel
+  // extraction.
+  virtual bool WriteBytes(const char* data, int num_bytes) = 0;
+};
+
 // This class is used for reading zip files. A typical use case of this
 // class is to scan entries in a zip file and extract them. The code will
 // look like:
@@ -141,6 +156,9 @@
   // success. On failure, current_entry_info() becomes NULL.
   bool LocateAndOpenEntry(const base::FilePath& path_in_zip);
 
+  // Extracts the current entry in chunks to |delegate|.
+  bool ExtractCurrentEntry(WriterDelegate* delegate) const;
+
   // Extracts the current entry to the given output file path. If the
   // current file is a directory, just creates a directory
   // instead. Returns true on success. OpenCurrentEntryInZip() must be
@@ -148,7 +166,8 @@
   //
   // This function preserves the timestamp of the original entry. If that
   // timestamp is not valid, the timestamp will be set to the current time.
-  bool ExtractCurrentEntryToFilePath(const base::FilePath& output_file_path);
+  bool ExtractCurrentEntryToFilePath(
+      const base::FilePath& output_file_path) const;
 
   // Asynchronously extracts the current entry to the given output file path.
   // If the current entry is a directory it just creates the directory
@@ -174,13 +193,11 @@
   // This function preserves the timestamp of the original entry. If that
   // timestamp is not valid, the timestamp will be set to the current time.
   bool ExtractCurrentEntryIntoDirectory(
-      const base::FilePath& output_directory_path);
+      const base::FilePath& output_directory_path) const;
 
-#if defined(OS_POSIX)
-  // Extracts the current entry by writing directly to a file descriptor.
-  // Does not close the file descriptor. Returns true on success.
-  bool ExtractCurrentEntryToFd(int fd);
-#endif
+  // Extracts the current entry by writing directly to a platform file.
+  // Does not close the file. Returns true on success.
+  bool ExtractCurrentEntryToFile(base::File* file) const;
 
   // Extracts the current entry into memory. If the current entry is a directory
   // the |output| parameter is set to the empty string. If the current entry is
@@ -232,6 +249,30 @@
   DISALLOW_COPY_AND_ASSIGN(ZipReader);
 };
 
+// A writer delegate that writes to a given File.
+class FileWriterDelegate : public WriterDelegate {
+ public:
+  explicit FileWriterDelegate(base::File* file);
+
+  // Truncates the file to the number of bytes written.
+  ~FileWriterDelegate() override;
+
+  // WriterDelegate methods:
+
+  // Seeks to the beginning of the file, returning false if the seek fails.
+  bool PrepareOutput() override;
+
+  // Writes |num_bytes| bytes of |data| to the file, returning false on error or
+  // if not all bytes could be written.
+  bool WriteBytes(const char* data, int num_bytes) override;
+
+ private:
+  base::File* file_;
+  int64_t file_length_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileWriterDelegate);
+};
+
 }  // namespace zip
 
 #endif  // THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
diff --git a/third_party/zlib/google/zip_reader_unittest.cc b/third_party/zlib/google/zip_reader_unittest.cc
index d4bb579..89b4ac5 100644
--- a/third_party/zlib/google/zip_reader_unittest.cc
+++ b/third_party/zlib/google/zip_reader_unittest.cc
@@ -18,10 +18,14 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/zlib/google/zip_internal.h"
 
+using ::testing::Return;
+using ::testing::_;
+
 namespace {
 
 const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
@@ -47,6 +51,8 @@
 
   base::PlatformFile platform_file() { return file_.GetPlatformFile(); }
 
+  base::File* file() { return &file_; }
+
  private:
   base::File file_;
 };
@@ -93,6 +99,12 @@
   int64 current_progress_;
 };
 
+class MockWriterDelegate : public zip::WriterDelegate {
+ public:
+  MOCK_METHOD0(PrepareOutput, bool());
+  MOCK_METHOD2(WriteBytes, bool(const char*, int));
+};
+
 }   // namespace
 
 namespace zip {
@@ -287,8 +299,7 @@
   EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
 }
 
-#if defined(OS_POSIX)
-TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) {
+TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFile_RegularFile) {
   ZipReader reader;
   FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
   ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
@@ -296,18 +307,16 @@
   base::FilePath out_path = test_dir_.AppendASCII("quux.txt");
   FileWrapper out_fd_w(out_path, FileWrapper::READ_WRITE);
   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
-  ASSERT_TRUE(reader.ExtractCurrentEntryToFd(out_fd_w.platform_file()));
+  ASSERT_TRUE(reader.ExtractCurrentEntryToFile(out_fd_w.file()));
   // Read the output file and compute the MD5.
   std::string output;
-  ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
-                                     &output));
+  ASSERT_TRUE(base::ReadFileToString(out_path, &output));
   const std::string md5 = base::MD5String(output);
   EXPECT_EQ(kQuuxExpectedMD5, md5);
   // quux.txt should be larger than kZipBufSize so that we can exercise
   // the loop in ExtractCurrentEntry().
   EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
 }
-#endif
 
 TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_Directory) {
   ZipReader reader;
@@ -582,4 +591,97 @@
   }
 }
 
+// Test that when WriterDelegate::PrepareMock returns false, no other methods on
+// the delegate are called and the extraction fails.
+TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) {
+  testing::StrictMock<MockWriterDelegate> mock_writer;
+
+  EXPECT_CALL(mock_writer, PrepareOutput())
+      .WillOnce(Return(false));
+
+  base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
+  ZipReader reader;
+
+  ASSERT_TRUE(reader.Open(test_zip_file_));
+  ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
+  ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer));
+}
+
+// Test that when WriterDelegate::WriteBytes returns false, no other methods on
+// the delegate are called and the extraction fails.
+TEST_F(ZipReaderTest, ExtractCurrentEntryWriteBytesFailure) {
+  testing::StrictMock<MockWriterDelegate> mock_writer;
+
+  EXPECT_CALL(mock_writer, PrepareOutput())
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_writer, WriteBytes(_, _))
+      .WillOnce(Return(false));
+
+  base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
+  ZipReader reader;
+
+  ASSERT_TRUE(reader.Open(test_zip_file_));
+  ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
+  ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer));
+}
+
+// Test that extraction succeeds when the writer delegate reports all is well.
+TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) {
+  testing::StrictMock<MockWriterDelegate> mock_writer;
+
+  EXPECT_CALL(mock_writer, PrepareOutput())
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_writer, WriteBytes(_, _))
+      .WillRepeatedly(Return(true));
+
+  base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
+  ZipReader reader;
+
+  ASSERT_TRUE(reader.Open(test_zip_file_));
+  ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
+  ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer));
+}
+
+class FileWriterDelegateTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
+    file_.Initialize(temp_file_path_, (base::File::FLAG_CREATE_ALWAYS |
+                                       base::File::FLAG_READ |
+                                       base::File::FLAG_WRITE |
+                                       base::File::FLAG_TEMPORARY |
+                                       base::File::FLAG_DELETE_ON_CLOSE));
+    ASSERT_TRUE(file_.IsValid());
+  }
+
+  // Writes data to the file, leaving the current position at the end of the
+  // write.
+  void PopulateFile() {
+    static const char kSomeData[] = "this sure is some data.";
+    static const size_t kSomeDataLen = sizeof(kSomeData) - 1;
+    ASSERT_NE(-1LL, file_.Write(0LL, kSomeData, kSomeDataLen));
+  }
+
+  base::FilePath temp_file_path_;
+  base::File file_;
+};
+
+TEST_F(FileWriterDelegateTest, WriteToStartAndTruncate) {
+  // Write stuff and advance.
+  PopulateFile();
+
+  // This should rewind, write, then truncate.
+  static const char kSomeData[] = "short";
+  static const int kSomeDataLen = sizeof(kSomeData) - 1;
+  {
+    FileWriterDelegate writer(&file_);
+    ASSERT_TRUE(writer.PrepareOutput());
+    ASSERT_TRUE(writer.WriteBytes(kSomeData, kSomeDataLen));
+  }
+  ASSERT_EQ(kSomeDataLen, file_.GetLength());
+  char buf[kSomeDataLen] = {};
+  ASSERT_EQ(kSomeDataLen, file_.Read(0LL, buf, kSomeDataLen));
+  ASSERT_EQ(std::string(kSomeData), std::string(buf, kSomeDataLen));
+}
+
 }  // namespace zip
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 2cc6958..7c4a214 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 = '231949'
+  LLVM_WIN_REVISION = '232554'
 
 # Path constants. (All of these should be absolute paths.)
 THIS_DIR = os.path.abspath(os.path.dirname(__file__))
diff --git a/tools/idl_parser/idl_parser.py b/tools/idl_parser/idl_parser.py
index bdd6dc7..27b1914 100755
--- a/tools/idl_parser/idl_parser.py
+++ b/tools/idl_parser/idl_parser.py
@@ -273,6 +273,7 @@
   def p_InterfaceMember(self, p):
     """InterfaceMember : Const
                        | Operation
+                       | Serializer
                        | Stringifier
                        | StaticMember
                        | Iterable
@@ -467,7 +468,74 @@
     p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'),
                           self.BuildAttribute('VALUE', val))
 
-  # [30-34] NOT IMPLEMENTED (Serializer)
+  # [30]
+  def p_Serializer(self, p):
+    """Serializer : SERIALIZER SerializerRest"""
+    p[0] = self.BuildProduction('Serializer', p, 1, p[2])
+
+  # [31]
+  # TODO(jl): This adds ReturnType and ';', missing from the spec's grammar.
+  # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
+  def p_SerializerRest(self, p):
+    """SerializerRest : ReturnType OperationRest
+                      | '=' SerializationPattern ';'
+                      | ';'"""
+    if len(p) == 3:
+      p[2].AddChildren(p[1])
+      p[0] = p[2]
+    elif len(p) == 4:
+      p[0] = p[2]
+
+  # [32]
+  def p_SerializationPattern(self, p):
+    """SerializationPattern : '{' SerializationPatternMap '}'
+                            | '[' SerializationPatternList ']'
+                            | identifier"""
+    if len(p) > 2:
+      p[0] = p[2]
+    else:
+      p[0] = self.BuildAttribute('ATTRIBUTE', p[1])
+
+  # [33]
+  # TODO(jl): This adds the "ATTRIBUTE" and "INHERIT ',' ATTRIBUTE" variants,
+  # missing from the spec's grammar.
+  # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
+  def p_SerializationPatternMap(self, p):
+    """SerializationPatternMap : GETTER
+                               | ATTRIBUTE
+                               | INHERIT ',' ATTRIBUTE
+                               | INHERIT Identifiers
+                               | identifier Identifiers
+                               |"""
+    p[0] = self.BuildProduction('Map', p, 0)
+    if len(p) == 4:
+      p[0].AddChildren(self.BuildTrue('INHERIT'))
+      p[0].AddChildren(self.BuildTrue('ATTRIBUTE'))
+    elif len(p) > 1:
+      if p[1] == 'getter':
+        p[0].AddChildren(self.BuildTrue('GETTER'))
+      elif p[1] == 'attribute':
+        p[0].AddChildren(self.BuildTrue('ATTRIBUTE'))
+      else:
+        if p[1] == 'inherit':
+          p[0].AddChildren(self.BuildTrue('INHERIT'))
+          attributes = p[2]
+        else:
+          attributes = ListFromConcat(p[1], p[2])
+        p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes))
+
+  # [34]
+  def p_SerializationPatternList(self, p):
+    """SerializationPatternList : GETTER
+                                | identifier Identifiers
+                                |"""
+    p[0] = self.BuildProduction('List', p, 0)
+    if len(p) > 1:
+      if p[1] == 'getter':
+        p[0].AddChildren(self.BuildTrue('GETTER'))
+      else:
+        attributes = ListFromConcat(p[1], p[2])
+        p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes))
 
   # [35]
   def p_Stringifier(self, p):
diff --git a/tools/idl_parser/test_parser/interface_web.idl b/tools/idl_parser/test_parser/interface_web.idl
index b1f3278..90b0e83 100644
--- a/tools/idl_parser/test_parser/interface_web.idl
+++ b/tools/idl_parser/test_parser/interface_web.idl
@@ -323,3 +323,49 @@
   readonly setlike<long>;
   setlike<double>;
 };
+
+/* TREE
+ *Interface(MyIfaceSerializer)
+ *  Serializer()
+ *  Serializer()
+ *    Operation(toJSON)
+ *      Arguments()
+ *      Type()
+ *        Any()
+ *  Serializer()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    Map()
+ *  Serializer()
+ *    List()
+ *  Serializer()
+ *    List()
+ *  Serializer()
+ *    List()
+ */
+interface MyIfaceSerializer {
+  serializer;
+  serializer any toJSON();
+  serializer = name;
+  serializer = {};
+  serializer = { getter };
+  serializer = { attribute };
+  serializer = { inherit, attribute };
+  serializer = { inherit };
+  serializer = { inherit, name1, name2 };
+  serializer = { name1, name2 };
+  serializer = [];
+  serializer = [getter];
+  serializer = [name1, name2];
+};
diff --git a/tools/valgrind/asan/asan_symbolize.py b/tools/valgrind/asan/asan_symbolize.py
index 898edf4..0107406 100755
--- a/tools/valgrind/asan/asan_symbolize.py
+++ b/tools/valgrind/asan/asan_symbolize.py
@@ -246,9 +246,6 @@
 
   disable_buffering()
   set_symbolizer_path()
-  # Disallow fallback to addr2line/atos if llvm-symbolizer is not present. Those
-  # are slow and we don't want to use them ever.
-  asan_symbolize.allow_system_symbolizer = False
   asan_symbolize.demangle = True
   asan_symbolize.fix_filename_patterns = args.strip_path_prefix
   # Most source paths for Chromium binaries start with
diff --git a/tools/valgrind/asan/third_party/README.chromium b/tools/valgrind/asan/third_party/README.chromium
index 63a1761..5c363ea 100644
--- a/tools/valgrind/asan/third_party/README.chromium
+++ b/tools/valgrind/asan/third_party/README.chromium
@@ -1,6 +1,6 @@
 Name: asan_symbolize.py
 License: University of Illinois Open Source License.
-Version: r231492
+Version: r227327
 URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/scripts/asan_symbolize.py?view=co&content-type=text%2Fplain
 Security Critical: no
 
diff --git a/tools/valgrind/asan/third_party/asan_symbolize.py b/tools/valgrind/asan/third_party/asan_symbolize.py
index b9d3ad3..59fceaa 100755
--- a/tools/valgrind/asan/third_party/asan_symbolize.py
+++ b/tools/valgrind/asan/third_party/asan_symbolize.py
@@ -23,7 +23,6 @@
 binary_name_filter = None
 fix_filename_patterns = None
 logfile = sys.stdin
-allow_system_symbolizer = True
 
 # FIXME: merge the code that calls fix_filename().
 def fix_filename(file_name):
@@ -393,8 +392,6 @@
           [BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]])
     result = symbolizers[binary].symbolize(addr, binary, offset)
     if result is None:
-      if not allow_system_symbolizer:
-        raise Exception('Failed to launch or use llvm-symbolizer.')
       # Initialize system symbolizer only if other symbolizers failed.
       symbolizers[binary].append_symbolizer(
           SystemSymbolizerFactory(self.system, addr, binary))
diff --git a/tools/valgrind/drmemory/suppressions_full.txt b/tools/valgrind/drmemory/suppressions_full.txt
index dfe57e2..81fcb31 100644
--- a/tools/valgrind/drmemory/suppressions_full.txt
+++ b/tools/valgrind/drmemory/suppressions_full.txt
@@ -1900,3 +1900,26 @@
 *!std::char_traits<>::compare
 ...
 *!*::*URLRequest*::TestBody
+
+UNINITIALIZED READ
+name=bug_468169
+...
+*!vp9_foreach_transformed_block_in_plane
+...
+*!encode_superblock
+*!encode_b_rt
+*!encode_sb_rt
+*!encode_sb_rt
+*!encode_sb_rt
+*!encode_sb_rt
+*!nonrd_select_partition
+*!encode_nonrd_sb_row
+*!vp9_encode_tile
+
+UNINITIALIZED READ
+name=bug_468638
+*!egl::ConfigSorter::operator()
+*!std::_Insertion_sort1<>
+*!std::_Sort<>
+*!egl::ConfigSet::filter
+*!ConfigSetTest_*_BitSizes_Test::TestBody
diff --git a/tools/valgrind/gtest_exclude/browser_tests.gtest-memcheck.txt b/tools/valgrind/gtest_exclude/browser_tests.gtest-memcheck.txt
index 5abd383..337b367 100644
--- a/tools/valgrind/gtest_exclude/browser_tests.gtest-memcheck.txt
+++ b/tools/valgrind/gtest_exclude/browser_tests.gtest-memcheck.txt
@@ -43,8 +43,6 @@
 NewTabUIBrowserTest.LoadNTPInExistingProcess
 OutOfProcessPPAPITest.NetAddressPrivate_GetAnyAddress
 OutOfProcessPPAPITest.NetAddressPrivate_ReplacePort
-PageCyclerCachedBrowserTest.PlaybackMode
-PageCyclerCachedBrowserTest.URLNotInCache
 PPAPITest.ImeInputEvent
 PrerenderBrowserTest.*
 PrerenderBrowserTestWithNaCl.PrerenderNaClPluginEnabled
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index 121f26b..4374a72 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -2151,6 +2151,7 @@
    fun:_ZN3WTF9BitVector15resizeOutOfLineEm
    fun:_ZN3WTF9BitVector10ensureSizeEm
    fun:_ZN3WTF9BitVectorC*
+   ...
    fun:_ZN5blink10UseCounter17recordMeasurementENS0_7FeatureE
 }
 {
@@ -3350,7 +3351,6 @@
    fun:_ZNK8SkBitmap12copyPixelsToEPvmmb
    fun:_ZN5blink19ImageFrameGenerator14decodeAndScaleERK11SkImageInfomPvm
    fun:_ZN5blink22DecodingImageGenerator11onGetPixelsERK11SkImageInfoPvmPjPi
-   fun:_ZN16SkImageGenerator9getPixelsERK11SkImageInfoPvmPjPi
 }
 {
    bug_424056a
@@ -3628,6 +3628,7 @@
    bug_455732
    Memcheck:Leak
    fun:_Znw*
+   ...
    fun:_ZN4mojo7BindingINS_15ServiceProviderEE4BindENS_16ScopedHandleBaseINS_17MessagePipeHandleEEEPK15MojoAsyncWaiter
    fun:_ZN4mojo7BindingINS_15ServiceProviderEE4BindENS_16InterfaceRequestIS1_EEPK15MojoAsyncWaiter
    fun:_ZN7content19ServiceRegistryImpl4BindEN4mojo16InterfaceRequestINS1_15ServiceProviderEEE
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index 12a44cc..ae1132e 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -183,6 +183,8 @@
   }
   if (is_win) {
     sources += [
+      "angle_platform_impl.cc",
+      "angle_platform_impl.h",
       "gl_bindings_autogen_wgl.cc",
       "gl_bindings_autogen_wgl.h",
       "gl_context_wgl.cc",
diff --git a/ui/gl/DEPS b/ui/gl/DEPS
index 862c555..e582c0d 100644
--- a/ui/gl/DEPS
+++ b/ui/gl/DEPS
@@ -12,5 +12,9 @@
 # get access to desktop OpenGL.
   "gl_surface_osmesa.cc": [
     "+third_party/mesa/src/include/GL/osmesa.h",
-  ]
+  ],
+# Allow us to include ANGLE's base platform implementation.
+  "angle_platform_impl.h": [
+    "+third_party/angle/include/platform/Platform.h",
+  ],
 }
diff --git a/ui/gl/angle_platform_impl.cc b/ui/gl/angle_platform_impl.cc
new file mode 100644
index 0000000..2ce12b8
--- /dev/null
+++ b/ui/gl/angle_platform_impl.cc
@@ -0,0 +1,50 @@
+// 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 "ui/gl/angle_platform_impl.h"
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+
+namespace gfx {
+
+ANGLEPlatformImpl::ANGLEPlatformImpl() {
+}
+
+ANGLEPlatformImpl::~ANGLEPlatformImpl() {
+}
+
+void ANGLEPlatformImpl::histogramCustomCounts(const char* name,
+                                              int sample,
+                                              int min,
+                                              int max,
+                                              int bucket_count) {
+  // Copied from histogram macro, but without the static variable caching
+  // the histogram because name is dynamic.
+  base::HistogramBase* counter = base::Histogram::FactoryGet(
+      name, min, max, bucket_count,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  DCHECK_EQ(name, counter->histogram_name());
+  counter->Add(sample);
+}
+
+void ANGLEPlatformImpl::histogramEnumeration(const char* name,
+                                             int sample,
+                                             int boundary_value) {
+  // Copied from histogram macro, but without the static variable caching
+  // the histogram because name is dynamic.
+  base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
+      name, 1, boundary_value, boundary_value + 1,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  DCHECK_EQ(name, counter->histogram_name());
+  counter->Add(sample);
+}
+
+void ANGLEPlatformImpl::histogramSparse(const char* name, int sample) {
+  // For sparse histograms, we can use the macro, as it does not incorporate a
+  // static.
+  UMA_HISTOGRAM_SPARSE_SLOWLY(name, sample);
+}
+
+}  // namespace gfx
diff --git a/ui/gl/angle_platform_impl.h b/ui/gl/angle_platform_impl.h
new file mode 100644
index 0000000..2d12f60
--- /dev/null
+++ b/ui/gl/angle_platform_impl.h
@@ -0,0 +1,39 @@
+// 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 UI_GL_ANGLE_PLATFORM_IMPL_H_
+#define UI_GL_ANGLE_PLATFORM_IMPL_H_
+
+// Implements the ANGLE platform interface, for functionality like
+// histograms and trace profiling.
+
+#include "base/macros.h"
+#include "third_party/angle/include/platform/Platform.h"
+
+namespace gfx {
+
+// Derives the base ANGLE platform and provides implementations
+class ANGLEPlatformImpl : public angle::Platform {
+ public:
+  ANGLEPlatformImpl();
+  ~ANGLEPlatformImpl() override;
+
+  // angle::Platform:
+  void histogramCustomCounts(const char* name,
+                             int sample,
+                             int min,
+                             int max,
+                             int bucket_count) override;
+  void histogramEnumeration(const char* name,
+                            int sample,
+                            int boundary_value) override;
+  void histogramSparse(const char* name, int sample) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ANGLEPlatformImpl);
+};
+
+}  // namespace gfx
+
+#endif  // UI_GL_ANGLE_PLATFORM_IMPL_H_
diff --git a/ui/gl/gl.gyp b/ui/gl/gl.gyp
index 0538107..d11014b 100644
--- a/ui/gl/gl.gyp
+++ b/ui/gl/gl.gyp
@@ -186,6 +186,8 @@
         }],
         ['OS=="win"', {
           'sources': [
+            'angle_platform_impl.cc',
+            'angle_platform_impl.h',
             'gl_bindings_autogen_wgl.cc',
             'gl_bindings_autogen_wgl.h',
             'gl_context_wgl.cc',
diff --git a/ui/gl/gl_implementation_win.cc b/ui/gl/gl_implementation_win.cc
index 477f5a5..49ffa89 100644
--- a/ui/gl/gl_implementation_win.cc
+++ b/ui/gl/gl_implementation_win.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/native_library.h"
 #include "base/path_service.h"
@@ -16,6 +17,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/windows_version.h"
+// TODO(jmadill): Apply to all platforms eventually
+#include "ui/gl/angle_platform_impl.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context_stub_with_extensions.h"
 #include "ui/gl/gl_egl_api_implementation.h"
@@ -97,6 +100,12 @@
     GetCategoryEnabledFlagFunc get_category_enabled_flag,
     AddTraceEventFunc add_trace_event_func);
 
+// TODO(jmadill): Apply to all platforms eventually
+base::LazyInstance<ANGLEPlatformImpl> g_angle_platform_impl =
+    LAZY_INSTANCE_INITIALIZER;
+
+ANGLEPlatformShutdownFunc g_angle_platform_shutdown = nullptr;
+
 }  // namespace
 
 void GetAllowedGLImplementations(std::vector<GLImplementation>* impls) {
@@ -204,6 +213,7 @@
 #endif
 
       if (!using_swift_shader) {
+        // TODO(jmadill): remove when platform impl supports tracing
         SetTraceFunctionPointersFunc set_trace_function_pointers =
             reinterpret_cast<SetTraceFunctionPointersFunc>(
                 base::GetFunctionPointerFromNativeLibrary(
@@ -212,6 +222,23 @@
           set_trace_function_pointers(&AngleGetTraceCategoryEnabledFlag,
                                       &AngleAddTraceEvent);
         }
+
+        // Init ANGLE platform here, before we call GetPlatformDisplay().
+        // TODO(jmadill): Apply to all platforms eventually
+        ANGLEPlatformInitializeFunc angle_platform_init =
+            reinterpret_cast<ANGLEPlatformInitializeFunc>(
+                base::GetFunctionPointerFromNativeLibrary(
+                    gles_library,
+                    "ANGLEPlatformInitialize"));
+        if (angle_platform_init) {
+          angle_platform_init(&g_angle_platform_impl.Get());
+
+          g_angle_platform_shutdown =
+              reinterpret_cast<ANGLEPlatformShutdownFunc>(
+                  base::GetFunctionPointerFromNativeLibrary(
+                      gles_library,
+                      "ANGLEPlatformShutdown"));
+        }
       }
 
       GLGetProcAddressProc get_proc_address =
@@ -348,6 +375,11 @@
 }
 
 void ClearGLBindings() {
+  // TODO(jmadill): Apply to all platforms eventually
+  if (g_angle_platform_shutdown) {
+    g_angle_platform_shutdown();
+  }
+
   ClearGLBindingsEGL();
   ClearGLBindingsGL();
   ClearGLBindingsOSMESA();